graphql 1.0.0 → 1.1.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.
- checksums.yaml +4 -4
- data/lib/graphql.rb +10 -0
- data/lib/graphql/base_type.rb +8 -5
- data/lib/graphql/compatibility.rb +3 -0
- data/lib/graphql/compatibility/execution_specification.rb +414 -0
- data/lib/graphql/compatibility/query_parser_specification.rb +117 -0
- data/lib/graphql/compatibility/query_parser_specification/parse_error_specification.rb +81 -0
- data/lib/graphql/compatibility/query_parser_specification/query_assertions.rb +78 -0
- data/lib/graphql/compatibility/schema_parser_specification.rb +239 -0
- data/lib/graphql/define/instance_definable.rb +53 -21
- data/lib/graphql/directive.rb +1 -1
- data/lib/graphql/enum_type.rb +31 -8
- data/lib/graphql/execution/directive_checks.rb +0 -6
- data/lib/graphql/input_object_type.rb +6 -4
- data/lib/graphql/introspection/arguments_field.rb +3 -1
- data/lib/graphql/introspection/enum_values_field.rb +10 -5
- data/lib/graphql/introspection/fields_field.rb +1 -1
- data/lib/graphql/introspection/input_fields_field.rb +2 -2
- data/lib/graphql/introspection/interfaces_field.rb +7 -1
- data/lib/graphql/introspection/possible_types_field.rb +1 -1
- data/lib/graphql/introspection/schema_type.rb +1 -1
- data/lib/graphql/introspection/type_by_name_field.rb +4 -2
- data/lib/graphql/introspection/type_type.rb +7 -6
- data/lib/graphql/language/lexer.rl +0 -4
- data/lib/graphql/language/parser.rb +1 -1
- data/lib/graphql/language/parser.y +1 -1
- data/lib/graphql/list_type.rb +3 -4
- data/lib/graphql/non_null_type.rb +4 -8
- data/lib/graphql/object_type.rb +5 -3
- data/lib/graphql/query.rb +48 -12
- data/lib/graphql/query/context.rb +7 -1
- data/lib/graphql/query/serial_execution/execution_context.rb +8 -3
- data/lib/graphql/query/serial_execution/field_resolution.rb +8 -5
- data/lib/graphql/query/serial_execution/operation_resolution.rb +2 -2
- data/lib/graphql/query/serial_execution/selection_resolution.rb +4 -21
- data/lib/graphql/query/serial_execution/value_resolution.rb +59 -99
- data/lib/graphql/query/variables.rb +7 -2
- data/lib/graphql/scalar_type.rb +1 -1
- data/lib/graphql/schema.rb +49 -18
- data/lib/graphql/schema/build_from_definition.rb +248 -0
- data/lib/graphql/schema/instrumented_field_map.rb +23 -0
- data/lib/graphql/schema/loader.rb +4 -11
- data/lib/graphql/schema/possible_types.rb +4 -2
- data/lib/graphql/schema/printer.rb +1 -1
- data/lib/graphql/schema/type_expression.rb +4 -4
- data/lib/graphql/schema/type_map.rb +1 -1
- data/lib/graphql/schema/validation.rb +4 -0
- data/lib/graphql/schema/warden.rb +114 -0
- data/lib/graphql/static_validation/literal_validator.rb +10 -7
- data/lib/graphql/static_validation/rules/argument_literals_are_compatible.rb +1 -3
- data/lib/graphql/static_validation/rules/arguments_are_defined.rb +1 -1
- data/lib/graphql/static_validation/rules/fields_are_defined_on_type.rb +1 -1
- data/lib/graphql/static_validation/rules/fragment_spreads_are_possible.rb +3 -14
- data/lib/graphql/static_validation/rules/fragment_types_exist.rb +1 -1
- data/lib/graphql/static_validation/rules/fragments_are_on_composite_types.rb +1 -1
- data/lib/graphql/static_validation/rules/variable_default_values_are_correctly_typed.rb +3 -4
- data/lib/graphql/static_validation/rules/variables_are_input_types.rb +2 -1
- data/lib/graphql/static_validation/validation_context.rb +7 -1
- data/lib/graphql/union_type.rb +6 -3
- data/lib/graphql/unresolved_type_error.rb +1 -2
- data/lib/graphql/version.rb +1 -1
- data/readme.md +1 -5
- data/spec/graphql/compatibility/execution_specification_spec.rb +3 -0
- data/spec/graphql/compatibility/query_parser_specification_spec.rb +5 -0
- data/spec/graphql/compatibility/schema_parser_specification_spec.rb +5 -0
- data/spec/graphql/define/instance_definable_spec.rb +20 -0
- data/spec/graphql/directive_spec.rb +11 -0
- data/spec/graphql/enum_type_spec.rb +20 -1
- data/spec/graphql/input_object_type_spec.rb +9 -9
- data/spec/graphql/introspection/directive_type_spec.rb +4 -4
- data/spec/graphql/introspection/input_value_type_spec.rb +6 -6
- data/spec/graphql/introspection/type_type_spec.rb +28 -26
- data/spec/graphql/language/parser_spec.rb +27 -17
- data/spec/graphql/list_type_spec.rb +2 -2
- data/spec/graphql/query/variables_spec.rb +1 -0
- data/spec/graphql/scalar_type_spec.rb +3 -3
- data/spec/graphql/schema/build_from_definition_spec.rb +693 -0
- data/spec/graphql/schema/type_expression_spec.rb +3 -3
- data/spec/graphql/schema/validation_spec.rb +7 -3
- data/spec/graphql/schema/warden_spec.rb +510 -0
- data/spec/graphql/schema_spec.rb +129 -0
- data/spec/graphql/static_validation/rules/argument_literals_are_compatible_spec.rb +1 -1
- data/spec/graphql/static_validation/type_stack_spec.rb +3 -3
- data/spec/spec_helper.rb +27 -1
- data/spec/support/dairy_app.rb +8 -5
- metadata +21 -3
- data/lib/graphql/language/parser_tests.rb +0 -809
data/spec/graphql/schema_spec.rb
CHANGED
@@ -124,4 +124,133 @@ describe GraphQL::Schema do
|
|
124
124
|
end
|
125
125
|
end
|
126
126
|
end
|
127
|
+
|
128
|
+
describe "directives" do
|
129
|
+
describe "when directives are not overwritten" do
|
130
|
+
it "contains built-in directives" do
|
131
|
+
schema = GraphQL::Schema.define
|
132
|
+
|
133
|
+
assert_equal ['deprecated', 'include', 'skip'], schema.directives.keys.sort
|
134
|
+
|
135
|
+
assert_equal GraphQL::Directive::DeprecatedDirective, schema.directives['deprecated']
|
136
|
+
assert_equal GraphQL::Directive::IncludeDirective, schema.directives['include']
|
137
|
+
assert_equal GraphQL::Directive::SkipDirective, schema.directives['skip']
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
describe "when directives are overwritten" do
|
142
|
+
it "contains only specified directives" do
|
143
|
+
schema = GraphQL::Schema.define do
|
144
|
+
directives [GraphQL::Directive::DeprecatedDirective]
|
145
|
+
end
|
146
|
+
|
147
|
+
assert_equal ['deprecated'], schema.directives.keys.sort
|
148
|
+
assert_equal GraphQL::Directive::DeprecatedDirective, schema.directives['deprecated']
|
149
|
+
end
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
describe ".from_definition" do
|
154
|
+
it "uses BuildFromSchema to build a schema from a definition string" do
|
155
|
+
schema = <<-SCHEMA
|
156
|
+
type Query {
|
157
|
+
str: String
|
158
|
+
}
|
159
|
+
SCHEMA
|
160
|
+
|
161
|
+
built_schema = GraphQL::Schema.from_definition(schema)
|
162
|
+
assert_equal schema.chop, GraphQL::Schema::Printer.print_schema(built_schema)
|
163
|
+
end
|
164
|
+
end
|
165
|
+
|
166
|
+
describe ".from_introspection" do
|
167
|
+
let(:schema) {
|
168
|
+
query_root = GraphQL::ObjectType.define do
|
169
|
+
name 'Query'
|
170
|
+
field :str, types.String
|
171
|
+
end
|
172
|
+
|
173
|
+
GraphQL::Schema.define do
|
174
|
+
query query_root
|
175
|
+
end
|
176
|
+
}
|
177
|
+
let(:schema_json) {
|
178
|
+
schema.execute(GraphQL::Introspection::INTROSPECTION_QUERY)
|
179
|
+
}
|
180
|
+
it "uses Schema::Loader to build a schema from an introspection result" do
|
181
|
+
built_schema = GraphQL::Schema.from_introspection(schema_json)
|
182
|
+
assert_equal GraphQL::Schema::Printer.print_schema(schema), GraphQL::Schema::Printer.print_schema(built_schema)
|
183
|
+
end
|
184
|
+
end
|
185
|
+
|
186
|
+
describe "#instrument" do
|
187
|
+
class MultiplyInstrumenter
|
188
|
+
def initialize(multiplier)
|
189
|
+
@multiplier = multiplier
|
190
|
+
end
|
191
|
+
|
192
|
+
def instrument(type_defn, field_defn)
|
193
|
+
if type_defn.name == "Query" && field_defn.name == "int"
|
194
|
+
prev_proc = field_defn.resolve_proc
|
195
|
+
new_resolve_proc = ->(obj, args, ctx) {
|
196
|
+
inner_value = prev_proc.call(obj, args, ctx)
|
197
|
+
inner_value * @multiplier
|
198
|
+
}
|
199
|
+
|
200
|
+
field_defn.redefine do
|
201
|
+
resolve(new_resolve_proc)
|
202
|
+
end
|
203
|
+
else
|
204
|
+
field_defn
|
205
|
+
end
|
206
|
+
end
|
207
|
+
end
|
208
|
+
|
209
|
+
class VariableCountInstrumenter
|
210
|
+
attr_reader :counts
|
211
|
+
def initialize
|
212
|
+
@counts = []
|
213
|
+
end
|
214
|
+
|
215
|
+
def before_query(query)
|
216
|
+
@counts << query.variables.length
|
217
|
+
end
|
218
|
+
|
219
|
+
def after_query(query)
|
220
|
+
end
|
221
|
+
end
|
222
|
+
|
223
|
+
let(:variable_counter) {
|
224
|
+
VariableCountInstrumenter.new
|
225
|
+
}
|
226
|
+
let(:query_type) {
|
227
|
+
GraphQL::ObjectType.define do
|
228
|
+
name "Query"
|
229
|
+
field :int, types.Int do
|
230
|
+
argument :value, types.Int
|
231
|
+
resolve -> (obj, args, ctx) { args[:value] }
|
232
|
+
end
|
233
|
+
end
|
234
|
+
}
|
235
|
+
|
236
|
+
let(:schema) {
|
237
|
+
spec = self
|
238
|
+
GraphQL::Schema.define do
|
239
|
+
query(spec.query_type)
|
240
|
+
instrument(:field, MultiplyInstrumenter.new(3))
|
241
|
+
instrument(:query, spec.variable_counter)
|
242
|
+
end
|
243
|
+
}
|
244
|
+
|
245
|
+
it "can modify field definitions" do
|
246
|
+
res = schema.execute(" { int(value: 2) } ")
|
247
|
+
assert_equal 6, res["data"]["int"]
|
248
|
+
end
|
249
|
+
|
250
|
+
it "can wrap query execution" do
|
251
|
+
schema.execute("query getInt($val: Int = 5){ int(value: $val) } ")
|
252
|
+
schema.execute("query getInt($val: Int = 5, $val2: Int = 3){ int(value: $val) int2: int(value: $val2) } ")
|
253
|
+
assert_equal [1, 2], variable_counter.counts
|
254
|
+
end
|
255
|
+
end
|
127
256
|
end
|
@@ -21,7 +21,7 @@ describe GraphQL::StaticValidation::ArgumentLiteralsAreCompatible do
|
|
21
21
|
|
22
22
|
it "finds undefined or missing-required arguments to fields and directives" do
|
23
23
|
# `wacky` above is handled by ArgumentsAreDefined, so only 6 are tested below
|
24
|
-
assert_equal(
|
24
|
+
assert_equal(8, errors.length)
|
25
25
|
|
26
26
|
query_root_error = {
|
27
27
|
"message"=>"Argument 'id' on Field 'stringCheese' has an invalid value. Expected type 'Int!'.",
|
@@ -8,7 +8,7 @@ class TypeCheckValidator
|
|
8
8
|
def validate(context)
|
9
9
|
self.class.checks.clear
|
10
10
|
context.visitor[GraphQL::Language::Nodes::Field] << ->(node, parent) {
|
11
|
-
self.class.checks << context.object_types.map
|
11
|
+
self.class.checks << context.object_types.map {|t| t.name || t.kind.name }
|
12
12
|
}
|
13
13
|
end
|
14
14
|
end
|
@@ -29,8 +29,8 @@ describe GraphQL::StaticValidation::TypeStack do
|
|
29
29
|
validator.validate(query)
|
30
30
|
expected = [
|
31
31
|
["Query", "Cheese"],
|
32
|
-
["Query", "Cheese", "
|
33
|
-
["Edible", "
|
32
|
+
["Query", "Cheese", "NON_NULL"],
|
33
|
+
["Edible", "NON_NULL"]
|
34
34
|
]
|
35
35
|
assert_equal(expected, TypeCheckValidator.checks)
|
36
36
|
end
|
data/spec/spec_helper.rb
CHANGED
@@ -16,7 +16,33 @@ Minitest::Spec.make_my_diffs_pretty!
|
|
16
16
|
# to be shown.
|
17
17
|
Minitest.backtrace_filter = Minitest::BacktraceFilter.new
|
18
18
|
|
19
|
-
|
19
|
+
|
20
|
+
# This is for convenient access to metadata in test definitions
|
21
|
+
assign_metadata_key = -> (target, key, value) { target.metadata[key] = value }
|
22
|
+
GraphQL::BaseType.accepts_definitions(metadata: assign_metadata_key)
|
23
|
+
GraphQL::Field.accepts_definitions(metadata: assign_metadata_key)
|
24
|
+
GraphQL::Argument.accepts_definitions(metadata: assign_metadata_key)
|
25
|
+
GraphQL::EnumType::EnumValue.accepts_definitions(metadata: assign_metadata_key)
|
26
|
+
|
27
|
+
# Can be used as a GraphQL::Schema::Warden for some purposes, but allows anything
|
28
|
+
module PermissiveWarden
|
29
|
+
def self.input_fields(input_obj)
|
30
|
+
input_obj.arguments.values
|
31
|
+
end
|
32
|
+
|
33
|
+
def self.enum_values(enum_type)
|
34
|
+
enum_type.values.values
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
# Can be used as a GraphQL::Schema::Warden for some purposes, but allows nothing
|
39
|
+
module NothingWarden
|
40
|
+
def self.enum_values(enum_type)
|
41
|
+
[]
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
# Load support files
|
20
46
|
Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each { |f| require f }
|
21
47
|
|
22
48
|
def star_wars_query(string, variables={})
|
data/spec/support/dairy_app.rb
CHANGED
@@ -33,10 +33,12 @@ end
|
|
33
33
|
DairyAnimalEnum = GraphQL::EnumType.define do
|
34
34
|
name "DairyAnimal"
|
35
35
|
description "An animal which can yield milk"
|
36
|
-
value("COW",
|
37
|
-
value("
|
38
|
-
value("
|
39
|
-
value("
|
36
|
+
value("COW", "Animal with black and white spots", value: 1)
|
37
|
+
value("DONKEY", "Animal with fur", value: :donkey)
|
38
|
+
value("GOAT", "Animal with horns")
|
39
|
+
value("REINDEER", "Animal with horns", value: 'reindeer')
|
40
|
+
value("SHEEP", "Animal with wool")
|
41
|
+
value("YAK", "Animal with long hair", deprecation_reason: "Out of fashion")
|
40
42
|
end
|
41
43
|
|
42
44
|
CheeseType = GraphQL::ObjectType.define do
|
@@ -57,7 +59,8 @@ CheeseType = GraphQL::ObjectType.define do
|
|
57
59
|
field :similarCheese, CheeseType, "Cheeses like this one", property: :this_should_be_overriden do
|
58
60
|
# metadata test
|
59
61
|
joins [:cheeses, :milks]
|
60
|
-
argument :source, !types[!DairyAnimalEnum]
|
62
|
+
argument :source, !types[!DairyAnimalEnum]
|
63
|
+
argument :nullableSource, types[!DairyAnimalEnum], default_value: [1]
|
61
64
|
resolve ->(t, a, c) {
|
62
65
|
# get the strings out:
|
63
66
|
sources = a["source"]
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: graphql
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Robert Mosolgo
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-
|
11
|
+
date: 2016-11-01 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: codeclimate-test-reporter
|
@@ -271,6 +271,12 @@ files:
|
|
271
271
|
- lib/graphql/argument.rb
|
272
272
|
- lib/graphql/base_type.rb
|
273
273
|
- lib/graphql/boolean_type.rb
|
274
|
+
- lib/graphql/compatibility.rb
|
275
|
+
- lib/graphql/compatibility/execution_specification.rb
|
276
|
+
- lib/graphql/compatibility/query_parser_specification.rb
|
277
|
+
- lib/graphql/compatibility/query_parser_specification/parse_error_specification.rb
|
278
|
+
- lib/graphql/compatibility/query_parser_specification/query_assertions.rb
|
279
|
+
- lib/graphql/compatibility/schema_parser_specification.rb
|
274
280
|
- lib/graphql/define.rb
|
275
281
|
- lib/graphql/define/assign_argument.rb
|
276
282
|
- lib/graphql/define/assign_connection.rb
|
@@ -330,7 +336,6 @@ files:
|
|
330
336
|
- lib/graphql/language/nodes.rb
|
331
337
|
- lib/graphql/language/parser.rb
|
332
338
|
- lib/graphql/language/parser.y
|
333
|
-
- lib/graphql/language/parser_tests.rb
|
334
339
|
- lib/graphql/language/token.rb
|
335
340
|
- lib/graphql/language/visitor.rb
|
336
341
|
- lib/graphql/list_type.rb
|
@@ -365,7 +370,9 @@ files:
|
|
365
370
|
- lib/graphql/relay/relation_connection.rb
|
366
371
|
- lib/graphql/scalar_type.rb
|
367
372
|
- lib/graphql/schema.rb
|
373
|
+
- lib/graphql/schema/build_from_definition.rb
|
368
374
|
- lib/graphql/schema/catchall_middleware.rb
|
375
|
+
- lib/graphql/schema/instrumented_field_map.rb
|
369
376
|
- lib/graphql/schema/invalid_type_error.rb
|
370
377
|
- lib/graphql/schema/loader.rb
|
371
378
|
- lib/graphql/schema/middleware_chain.rb
|
@@ -378,6 +385,7 @@ files:
|
|
378
385
|
- lib/graphql/schema/type_map.rb
|
379
386
|
- lib/graphql/schema/unique_within_type.rb
|
380
387
|
- lib/graphql/schema/validation.rb
|
388
|
+
- lib/graphql/schema/warden.rb
|
381
389
|
- lib/graphql/static_validation.rb
|
382
390
|
- lib/graphql/static_validation/all_rules.rb
|
383
391
|
- lib/graphql/static_validation/arguments_validator.rb
|
@@ -421,6 +429,9 @@ files:
|
|
421
429
|
- spec/graphql/argument_spec.rb
|
422
430
|
- spec/graphql/base_type_spec.rb
|
423
431
|
- spec/graphql/boolean_type_spec.rb
|
432
|
+
- spec/graphql/compatibility/execution_specification_spec.rb
|
433
|
+
- spec/graphql/compatibility/query_parser_specification_spec.rb
|
434
|
+
- spec/graphql/compatibility/schema_parser_specification_spec.rb
|
424
435
|
- spec/graphql/define/instance_definable_spec.rb
|
425
436
|
- spec/graphql/directive_spec.rb
|
426
437
|
- spec/graphql/enum_type_spec.rb
|
@@ -464,6 +475,7 @@ files:
|
|
464
475
|
- spec/graphql/relay/page_info_spec.rb
|
465
476
|
- spec/graphql/relay/relation_connection_spec.rb
|
466
477
|
- spec/graphql/scalar_type_spec.rb
|
478
|
+
- spec/graphql/schema/build_from_definition_spec.rb
|
467
479
|
- spec/graphql/schema/catchall_middleware_spec.rb
|
468
480
|
- spec/graphql/schema/loader_spec.rb
|
469
481
|
- spec/graphql/schema/middleware_chain_spec.rb
|
@@ -474,6 +486,7 @@ files:
|
|
474
486
|
- spec/graphql/schema/type_expression_spec.rb
|
475
487
|
- spec/graphql/schema/unique_within_type_spec.rb
|
476
488
|
- spec/graphql/schema/validation_spec.rb
|
489
|
+
- spec/graphql/schema/warden_spec.rb
|
477
490
|
- spec/graphql/schema_spec.rb
|
478
491
|
- spec/graphql/static_validation/rules/argument_literals_are_compatible_spec.rb
|
479
492
|
- spec/graphql/static_validation/rules/arguments_are_defined_spec.rb
|
@@ -540,6 +553,9 @@ test_files:
|
|
540
553
|
- spec/graphql/argument_spec.rb
|
541
554
|
- spec/graphql/base_type_spec.rb
|
542
555
|
- spec/graphql/boolean_type_spec.rb
|
556
|
+
- spec/graphql/compatibility/execution_specification_spec.rb
|
557
|
+
- spec/graphql/compatibility/query_parser_specification_spec.rb
|
558
|
+
- spec/graphql/compatibility/schema_parser_specification_spec.rb
|
543
559
|
- spec/graphql/define/instance_definable_spec.rb
|
544
560
|
- spec/graphql/directive_spec.rb
|
545
561
|
- spec/graphql/enum_type_spec.rb
|
@@ -583,6 +599,7 @@ test_files:
|
|
583
599
|
- spec/graphql/relay/page_info_spec.rb
|
584
600
|
- spec/graphql/relay/relation_connection_spec.rb
|
585
601
|
- spec/graphql/scalar_type_spec.rb
|
602
|
+
- spec/graphql/schema/build_from_definition_spec.rb
|
586
603
|
- spec/graphql/schema/catchall_middleware_spec.rb
|
587
604
|
- spec/graphql/schema/loader_spec.rb
|
588
605
|
- spec/graphql/schema/middleware_chain_spec.rb
|
@@ -593,6 +610,7 @@ test_files:
|
|
593
610
|
- spec/graphql/schema/type_expression_spec.rb
|
594
611
|
- spec/graphql/schema/unique_within_type_spec.rb
|
595
612
|
- spec/graphql/schema/validation_spec.rb
|
613
|
+
- spec/graphql/schema/warden_spec.rb
|
596
614
|
- spec/graphql/schema_spec.rb
|
597
615
|
- spec/graphql/static_validation/rules/argument_literals_are_compatible_spec.rb
|
598
616
|
- spec/graphql/static_validation/rules/arguments_are_defined_spec.rb
|
@@ -1,809 +0,0 @@
|
|
1
|
-
module GraphQL
|
2
|
-
module Language
|
3
|
-
# If you create your own GraphQL parser, can verify it using these tests.
|
4
|
-
#
|
5
|
-
# @example Include these tests in a Minitest suite
|
6
|
-
# require 'graphql/language/parser_tests'
|
7
|
-
#
|
8
|
-
# describe MyParser do
|
9
|
-
# include GraphQL::Language::ParserTests
|
10
|
-
# subject { MyParser }
|
11
|
-
# end
|
12
|
-
module ParserTests
|
13
|
-
def self.included(test)
|
14
|
-
test.send(:describe, "Parser Tests") do
|
15
|
-
let(:document) { subject.parse(query_string) }
|
16
|
-
let(:query_string) {%|
|
17
|
-
query getStuff($someVar: Int = 1, $anotherVar: [String!] ) @skip(if: false) {
|
18
|
-
myField: someField(someArg: $someVar, ok: 1.4) @skip(if: $anotherVar) @thing(or: "Whatever")
|
19
|
-
|
20
|
-
anotherField(someArg: [1,2,3]) {
|
21
|
-
nestedField
|
22
|
-
... moreNestedFields @skip(if: true)
|
23
|
-
}
|
24
|
-
|
25
|
-
... on OtherType @include(unless: false){
|
26
|
-
field(arg: [{key: "value", anotherKey: 0.9, anotherAnotherKey: WHATEVER}])
|
27
|
-
anotherField
|
28
|
-
}
|
29
|
-
|
30
|
-
... {
|
31
|
-
id
|
32
|
-
}
|
33
|
-
}
|
34
|
-
|
35
|
-
fragment moreNestedFields on NestedType @or(something: "ok") {
|
36
|
-
anotherNestedField @enum(directive: true)
|
37
|
-
}
|
38
|
-
|}
|
39
|
-
|
40
|
-
describe ".parse" do
|
41
|
-
it "parses queries" do
|
42
|
-
assert document
|
43
|
-
end
|
44
|
-
|
45
|
-
describe "visited nodes" do
|
46
|
-
let(:query) { document.definitions.first }
|
47
|
-
let(:fragment_def) { document.definitions.last }
|
48
|
-
|
49
|
-
it "creates a valid document" do
|
50
|
-
assert document.is_a?(GraphQL::Language::Nodes::Document)
|
51
|
-
assert_equal 2, document.definitions.length
|
52
|
-
end
|
53
|
-
|
54
|
-
it "creates a valid operation" do
|
55
|
-
assert query.is_a?(GraphQL::Language::Nodes::OperationDefinition)
|
56
|
-
assert_equal "getStuff", query.name
|
57
|
-
assert_equal "query", query.operation_type
|
58
|
-
assert_equal 2, query.variables.length
|
59
|
-
assert_equal 4, query.selections.length
|
60
|
-
assert_equal 1, query.directives.length
|
61
|
-
assert_equal [2, 13], [query.line, query.col]
|
62
|
-
end
|
63
|
-
|
64
|
-
it "creates a valid fragment definition" do
|
65
|
-
assert fragment_def.is_a?(GraphQL::Language::Nodes::FragmentDefinition)
|
66
|
-
assert_equal "moreNestedFields", fragment_def.name
|
67
|
-
assert_equal 1, fragment_def.selections.length
|
68
|
-
assert_equal "NestedType", fragment_def.type.name
|
69
|
-
assert_equal 1, fragment_def.directives.length
|
70
|
-
assert_equal [20, 13], fragment_def.position
|
71
|
-
end
|
72
|
-
|
73
|
-
describe "variable definitions" do
|
74
|
-
let(:optional_var) { query.variables.first }
|
75
|
-
it "gets name and type" do
|
76
|
-
assert_equal "someVar", optional_var.name
|
77
|
-
assert_equal "Int", optional_var.type.name
|
78
|
-
end
|
79
|
-
|
80
|
-
it "gets default value" do
|
81
|
-
assert_equal 1, optional_var.default_value
|
82
|
-
end
|
83
|
-
|
84
|
-
it "gets position info" do
|
85
|
-
assert_equal [2, 28], optional_var.position
|
86
|
-
end
|
87
|
-
end
|
88
|
-
|
89
|
-
describe "fields" do
|
90
|
-
let(:leaf_field) { query.selections.first }
|
91
|
-
let(:parent_field) { query.selections[1] }
|
92
|
-
|
93
|
-
it "gets name, alias, arguments and directives" do
|
94
|
-
assert_equal "someField", leaf_field.name
|
95
|
-
assert_equal "myField", leaf_field.alias
|
96
|
-
assert_equal 2, leaf_field.directives.length
|
97
|
-
assert_equal 2, leaf_field.arguments.length
|
98
|
-
end
|
99
|
-
|
100
|
-
it "gets nested fields" do
|
101
|
-
assert_equal 2, parent_field.selections.length
|
102
|
-
end
|
103
|
-
|
104
|
-
it "gets location info" do
|
105
|
-
assert_equal [3 ,15], leaf_field.position
|
106
|
-
end
|
107
|
-
|
108
|
-
describe "when the arguments list is empty" do
|
109
|
-
let(:query_string) { "{ field() }"}
|
110
|
-
let(:field) { query.selections.first }
|
111
|
-
it "has zero arguments" do
|
112
|
-
assert_equal 0, field.arguments.length
|
113
|
-
end
|
114
|
-
end
|
115
|
-
|
116
|
-
describe "when selections are empty" do
|
117
|
-
let(:query_string) { "{ field { } }"}
|
118
|
-
let(:field) { query.selections.first }
|
119
|
-
it "has zero selections" do
|
120
|
-
assert_equal 0, field.selections.length
|
121
|
-
end
|
122
|
-
end
|
123
|
-
end
|
124
|
-
|
125
|
-
describe "arguments" do
|
126
|
-
let(:literal_argument) { query.selections.first.arguments.last }
|
127
|
-
let(:variable_argument) { query.selections.first.arguments.first }
|
128
|
-
|
129
|
-
it "gets name and literal value" do
|
130
|
-
assert_equal "ok", literal_argument.name
|
131
|
-
assert_equal 1.4, literal_argument.value
|
132
|
-
end
|
133
|
-
|
134
|
-
it "gets name and variable value" do
|
135
|
-
assert_equal "someArg", variable_argument.name
|
136
|
-
assert_equal "someVar", variable_argument.value.name
|
137
|
-
end
|
138
|
-
|
139
|
-
|
140
|
-
it "gets position info" do
|
141
|
-
assert_equal [3, 34], variable_argument.position
|
142
|
-
end
|
143
|
-
end
|
144
|
-
|
145
|
-
describe "fragment spreads" do
|
146
|
-
let(:fragment_spread) { query.selections[1].selections.last }
|
147
|
-
it "gets the name and directives" do
|
148
|
-
assert_equal "moreNestedFields", fragment_spread.name
|
149
|
-
assert_equal 1, fragment_spread.directives.length
|
150
|
-
end
|
151
|
-
|
152
|
-
it "gets position info" do
|
153
|
-
assert_equal [7, 17], fragment_spread.position
|
154
|
-
end
|
155
|
-
end
|
156
|
-
|
157
|
-
describe "directives" do
|
158
|
-
let(:variable_directive) { query.selections.first.directives.first }
|
159
|
-
|
160
|
-
it "gets the name and arguments" do
|
161
|
-
assert_equal "skip", variable_directive.name
|
162
|
-
assert_equal "if", variable_directive.arguments.first.name
|
163
|
-
assert_equal 1, variable_directive.arguments.length
|
164
|
-
end
|
165
|
-
|
166
|
-
it "gets position info" do
|
167
|
-
assert_equal [3, 62], variable_directive.position
|
168
|
-
end
|
169
|
-
end
|
170
|
-
|
171
|
-
describe "inline fragments" do
|
172
|
-
let(:inline_fragment) { query.selections[2] }
|
173
|
-
let(:typeless_inline_fragment) { query.selections[3] }
|
174
|
-
|
175
|
-
it "gets the type and directives" do
|
176
|
-
assert_equal "OtherType", inline_fragment.type.name
|
177
|
-
assert_equal 2, inline_fragment.selections.length
|
178
|
-
assert_equal 1, inline_fragment.directives.length
|
179
|
-
end
|
180
|
-
|
181
|
-
it "gets inline fragments without type conditions" do
|
182
|
-
assert_equal nil, typeless_inline_fragment.type
|
183
|
-
assert_equal 1, typeless_inline_fragment.selections.length
|
184
|
-
assert_equal 0, typeless_inline_fragment.directives.length
|
185
|
-
end
|
186
|
-
|
187
|
-
it "gets position info" do
|
188
|
-
assert_equal [10, 15], inline_fragment.position
|
189
|
-
end
|
190
|
-
end
|
191
|
-
|
192
|
-
describe "inputs" do
|
193
|
-
let(:query_string) {%|
|
194
|
-
{
|
195
|
-
field(
|
196
|
-
int: 3,
|
197
|
-
float: 4.7e-24,
|
198
|
-
bool: false,
|
199
|
-
string: "☀︎🏆\\n escaped \\" unicode \\u00b6 /",
|
200
|
-
enum: ENUM_NAME,
|
201
|
-
array: [7, 8, 9]
|
202
|
-
object: {a: [1,2,3], b: {c: "4"}}
|
203
|
-
unicode_bom: "\xef\xbb\xbfquery"
|
204
|
-
keywordEnum: on
|
205
|
-
)
|
206
|
-
}
|
207
|
-
|}
|
208
|
-
|
209
|
-
let(:inputs) { document.definitions.first.selections.first.arguments }
|
210
|
-
|
211
|
-
it "parses ints" do
|
212
|
-
assert_equal 3, inputs[0].value
|
213
|
-
end
|
214
|
-
|
215
|
-
it "parses floats" do
|
216
|
-
assert_equal 0.47e-23, inputs[1].value
|
217
|
-
end
|
218
|
-
|
219
|
-
it "parses booleans" do
|
220
|
-
assert_equal false, inputs[2].value
|
221
|
-
end
|
222
|
-
|
223
|
-
it "parses UTF-8 strings" do
|
224
|
-
assert_equal %|☀︎🏆\n escaped " unicode ¶ /|, inputs[3].value
|
225
|
-
end
|
226
|
-
|
227
|
-
it "parses enums" do
|
228
|
-
assert_instance_of GraphQL::Language::Nodes::Enum, inputs[4].value
|
229
|
-
assert_equal "ENUM_NAME", inputs[4].value.name
|
230
|
-
end
|
231
|
-
|
232
|
-
it "parses arrays" do
|
233
|
-
assert_equal [7,8,9], inputs[5].value
|
234
|
-
end
|
235
|
-
|
236
|
-
it "parses objects" do
|
237
|
-
obj = inputs[6].value
|
238
|
-
assert_equal "a", obj.arguments[0].name
|
239
|
-
assert_equal [1,2,3], obj.arguments[0].value
|
240
|
-
assert_equal "b", obj.arguments[1].name
|
241
|
-
assert_equal "c", obj.arguments[1].value.arguments[0].name
|
242
|
-
assert_equal "4", obj.arguments[1].value.arguments[0].value
|
243
|
-
end
|
244
|
-
|
245
|
-
it "parses unicode bom" do
|
246
|
-
obj = inputs[7].value
|
247
|
-
assert_equal %|\xef\xbb\xbfquery|, inputs[7].value
|
248
|
-
end
|
249
|
-
|
250
|
-
it "parses enum 'on''" do
|
251
|
-
assert_equal "on", inputs[8].value.name
|
252
|
-
end
|
253
|
-
end
|
254
|
-
end
|
255
|
-
|
256
|
-
describe "unnamed queries" do
|
257
|
-
let(:query_string) {%|
|
258
|
-
{ name, age, height }
|
259
|
-
|}
|
260
|
-
let(:operation) { document.definitions.first }
|
261
|
-
|
262
|
-
it "parses unnamed queries" do
|
263
|
-
assert_equal 1, document.definitions.length
|
264
|
-
assert_equal "query", operation.operation_type
|
265
|
-
assert_equal nil, operation.name
|
266
|
-
assert_equal 3, operation.selections.length
|
267
|
-
end
|
268
|
-
end
|
269
|
-
|
270
|
-
describe "introspection query" do
|
271
|
-
let(:query_string) { GraphQL::Introspection::INTROSPECTION_QUERY }
|
272
|
-
|
273
|
-
it "parses a big ol' query" do
|
274
|
-
assert(document)
|
275
|
-
end
|
276
|
-
end
|
277
|
-
|
278
|
-
describe "schema with comments" do
|
279
|
-
let(:query_string) {%|
|
280
|
-
# Schema at beginning of file
|
281
|
-
|
282
|
-
schema {
|
283
|
-
query: Hello
|
284
|
-
}
|
285
|
-
|
286
|
-
# Comment between two definitions are omitted
|
287
|
-
|
288
|
-
# This is a directive
|
289
|
-
directive @foo(
|
290
|
-
# It has an argument
|
291
|
-
arg: Int
|
292
|
-
) on FIELD
|
293
|
-
|
294
|
-
# Multiline comment
|
295
|
-
#
|
296
|
-
# With an enum
|
297
|
-
enum Color {
|
298
|
-
RED
|
299
|
-
|
300
|
-
# Not a creative color
|
301
|
-
GREEN
|
302
|
-
BLUE
|
303
|
-
}
|
304
|
-
|
305
|
-
#Comment without preceding space
|
306
|
-
type Hello {
|
307
|
-
# And a field to boot
|
308
|
-
str: String
|
309
|
-
}
|
310
|
-
|
311
|
-
# Comment for input object types
|
312
|
-
input Car {
|
313
|
-
# Color of the car
|
314
|
-
color: String!
|
315
|
-
}
|
316
|
-
|
317
|
-
# Comment for interface definitions
|
318
|
-
interface Vehicle {
|
319
|
-
# Amount of wheels
|
320
|
-
wheels: Int!
|
321
|
-
}
|
322
|
-
|
323
|
-
# Comment at the end of schema
|
324
|
-
|}
|
325
|
-
|
326
|
-
it "parses successfully" do
|
327
|
-
document = subject.parse(query_string)
|
328
|
-
|
329
|
-
assert_equal 6, document.definitions.size
|
330
|
-
|
331
|
-
schema_definition = document.definitions.shift
|
332
|
-
assert_equal GraphQL::Language::Nodes::SchemaDefinition, schema_definition.class
|
333
|
-
|
334
|
-
directive_definition = document.definitions.shift
|
335
|
-
assert_equal GraphQL::Language::Nodes::DirectiveDefinition, directive_definition.class
|
336
|
-
assert_equal 'This is a directive', directive_definition.description
|
337
|
-
|
338
|
-
enum_type_definition = document.definitions.shift
|
339
|
-
assert_equal GraphQL::Language::Nodes::EnumTypeDefinition, enum_type_definition.class
|
340
|
-
assert_equal "Multiline comment\n\nWith an enum", enum_type_definition.description
|
341
|
-
|
342
|
-
assert_nil enum_type_definition.values[0].description
|
343
|
-
assert_equal 'Not a creative color', enum_type_definition.values[1].description
|
344
|
-
|
345
|
-
object_type_definition = document.definitions.shift
|
346
|
-
assert_equal GraphQL::Language::Nodes::ObjectTypeDefinition, object_type_definition.class
|
347
|
-
assert_equal 'Comment without preceding space', object_type_definition.description
|
348
|
-
assert_equal 'And a field to boot', object_type_definition.fields[0].description
|
349
|
-
|
350
|
-
input_object_type_definition = document.definitions.shift
|
351
|
-
assert_equal GraphQL::Language::Nodes::InputObjectTypeDefinition, input_object_type_definition.class
|
352
|
-
assert_equal 'Comment for input object types', input_object_type_definition.description
|
353
|
-
assert_equal 'Color of the car', input_object_type_definition.fields[0].description
|
354
|
-
|
355
|
-
interface_type_definition = document.definitions.shift
|
356
|
-
assert_equal GraphQL::Language::Nodes::InterfaceTypeDefinition, interface_type_definition.class
|
357
|
-
assert_equal 'Comment for interface definitions', interface_type_definition.description
|
358
|
-
assert_equal 'Amount of wheels', interface_type_definition.fields[0].description
|
359
|
-
end
|
360
|
-
end
|
361
|
-
|
362
|
-
describe "schema" do
|
363
|
-
it "parses the test schema" do
|
364
|
-
schema = DummySchema
|
365
|
-
schema_string = GraphQL::Schema::Printer.print_schema(schema)
|
366
|
-
|
367
|
-
document = subject.parse(schema_string)
|
368
|
-
|
369
|
-
assert_equal schema_string, document.to_query_string
|
370
|
-
end
|
371
|
-
|
372
|
-
it "parses mimal schema definition" do
|
373
|
-
document = subject.parse('schema { query: QueryRoot }')
|
374
|
-
|
375
|
-
schema = document.definitions.first
|
376
|
-
assert_equal 'QueryRoot', schema.query
|
377
|
-
assert_equal nil, schema.mutation
|
378
|
-
assert_equal nil, schema.subscription
|
379
|
-
end
|
380
|
-
|
381
|
-
it "parses full schema definitions" do
|
382
|
-
document = subject.parse('
|
383
|
-
schema {
|
384
|
-
query: QueryRoot
|
385
|
-
mutation: MutationRoot
|
386
|
-
subscription: SubscriptionRoot
|
387
|
-
}
|
388
|
-
')
|
389
|
-
|
390
|
-
schema = document.definitions.first
|
391
|
-
assert_equal 'QueryRoot', schema.query
|
392
|
-
assert_equal 'MutationRoot', schema.mutation
|
393
|
-
assert_equal 'SubscriptionRoot', schema.subscription
|
394
|
-
end
|
395
|
-
|
396
|
-
it "parses object types" do
|
397
|
-
document = subject.parse('
|
398
|
-
type Comment implements Node {
|
399
|
-
id: ID!
|
400
|
-
}
|
401
|
-
')
|
402
|
-
|
403
|
-
type = document.definitions.first
|
404
|
-
assert_equal GraphQL::Language::Nodes::ObjectTypeDefinition, type.class
|
405
|
-
assert_equal 'Comment', type.name
|
406
|
-
assert_equal ['Node'], type.interfaces.map(&:name)
|
407
|
-
assert_equal ['id'], type.fields.map(&:name)
|
408
|
-
assert_equal [], type.fields[0].arguments
|
409
|
-
assert_equal 'ID', type.fields[0].type.of_type.name
|
410
|
-
end
|
411
|
-
|
412
|
-
it "parses object types with directives" do
|
413
|
-
document = subject.parse('
|
414
|
-
type Comment implements Node @deprecated(reason: "No longer supported") {
|
415
|
-
id: ID!
|
416
|
-
}
|
417
|
-
')
|
418
|
-
|
419
|
-
type = document.definitions.first
|
420
|
-
assert_equal GraphQL::Language::Nodes::ObjectTypeDefinition, type.class
|
421
|
-
assert_equal 'Comment', type.name
|
422
|
-
assert_equal ['Node'], type.interfaces.map(&:name)
|
423
|
-
assert_equal ['id'], type.fields.map(&:name)
|
424
|
-
assert_equal [], type.fields[0].arguments
|
425
|
-
assert_equal 'ID', type.fields[0].type.of_type.name
|
426
|
-
assert_equal 1, type.directives.length
|
427
|
-
|
428
|
-
deprecated_directive = type.directives[0]
|
429
|
-
assert_equal 'deprecated', deprecated_directive.name
|
430
|
-
assert_equal 'reason', deprecated_directive.arguments[0].name
|
431
|
-
assert_equal 'No longer supported', deprecated_directive.arguments[0].value
|
432
|
-
end
|
433
|
-
|
434
|
-
it "parses field arguments" do
|
435
|
-
document = subject.parse('
|
436
|
-
type Mutation {
|
437
|
-
post(id: ID!, data: PostData = { message: "First!1!", type: BLOG, tags: ["Test", "Annoying"] }): Post
|
438
|
-
}
|
439
|
-
')
|
440
|
-
|
441
|
-
field = document.definitions.first.fields.first
|
442
|
-
assert_equal ['id', 'data'], field.arguments.map(&:name)
|
443
|
-
data_arg = field.arguments[1]
|
444
|
-
assert_equal 'PostData', data_arg.type.name
|
445
|
-
assert_equal ['message', 'type', 'tags'], data_arg.default_value.arguments.map(&:name)
|
446
|
-
tags_arg = data_arg.default_value.arguments[2]
|
447
|
-
assert_equal ['Test', 'Annoying'], tags_arg.value
|
448
|
-
end
|
449
|
-
|
450
|
-
it "parses field arguments with directives" do
|
451
|
-
document = subject.parse('
|
452
|
-
type Mutation {
|
453
|
-
post(id: ID! @deprecated(reason: "No longer supported"), data: String): Post
|
454
|
-
}
|
455
|
-
')
|
456
|
-
|
457
|
-
field = document.definitions.first.fields.first
|
458
|
-
assert_equal ['id', 'data'], field.arguments.map(&:name)
|
459
|
-
id_arg = field.arguments[0]
|
460
|
-
|
461
|
-
deprecated_directive = id_arg.directives[0]
|
462
|
-
assert_equal 'deprecated', deprecated_directive.name
|
463
|
-
assert_equal 'reason', deprecated_directive.arguments[0].name
|
464
|
-
assert_equal 'No longer supported', deprecated_directive.arguments[0].value
|
465
|
-
end
|
466
|
-
|
467
|
-
it "parses directive definition" do
|
468
|
-
document = subject.parse('
|
469
|
-
directive @include(if: Boolean!)
|
470
|
-
on FIELD
|
471
|
-
| FRAGMENT_SPREAD
|
472
|
-
| INLINE_FRAGMENT
|
473
|
-
')
|
474
|
-
|
475
|
-
type = document.definitions.first
|
476
|
-
assert_equal GraphQL::Language::Nodes::DirectiveDefinition, type.class
|
477
|
-
assert_equal 'include', type.name
|
478
|
-
|
479
|
-
assert_equal 1, type.arguments.length
|
480
|
-
assert_equal 'if', type.arguments[0].name
|
481
|
-
assert_equal 'Boolean', type.arguments[0].type.of_type.name
|
482
|
-
|
483
|
-
assert_equal ['FIELD', 'FRAGMENT_SPREAD', 'INLINE_FRAGMENT'], type.locations
|
484
|
-
end
|
485
|
-
|
486
|
-
it "parses scalar types" do
|
487
|
-
document = subject.parse('scalar DateTime')
|
488
|
-
|
489
|
-
type = document.definitions.first
|
490
|
-
assert_equal GraphQL::Language::Nodes::ScalarTypeDefinition, type.class
|
491
|
-
assert_equal 'DateTime', type.name
|
492
|
-
end
|
493
|
-
|
494
|
-
it "parses scalar types with directives" do
|
495
|
-
document = subject.parse('scalar DateTime @deprecated(reason: "No longer supported")')
|
496
|
-
|
497
|
-
type = document.definitions.first
|
498
|
-
assert_equal GraphQL::Language::Nodes::ScalarTypeDefinition, type.class
|
499
|
-
assert_equal 'DateTime', type.name
|
500
|
-
assert_equal 1, type.directives.length
|
501
|
-
|
502
|
-
deprecated_directive = type.directives[0]
|
503
|
-
assert_equal 'deprecated', deprecated_directive.name
|
504
|
-
assert_equal 'reason', deprecated_directive.arguments[0].name
|
505
|
-
assert_equal 'No longer supported', deprecated_directive.arguments[0].value
|
506
|
-
end
|
507
|
-
|
508
|
-
it "parses interface types" do
|
509
|
-
document = subject.parse('
|
510
|
-
interface Node {
|
511
|
-
id: ID!
|
512
|
-
}
|
513
|
-
')
|
514
|
-
|
515
|
-
type = document.definitions.first
|
516
|
-
assert_equal GraphQL::Language::Nodes::InterfaceTypeDefinition, type.class
|
517
|
-
assert_equal 'Node', type.name
|
518
|
-
assert_equal ['id'], type.fields.map(&:name)
|
519
|
-
assert_equal [], type.fields[0].arguments
|
520
|
-
assert_equal 'ID', type.fields[0].type.of_type.name
|
521
|
-
end
|
522
|
-
|
523
|
-
it "parses interface types with directives" do
|
524
|
-
document = subject.parse('
|
525
|
-
interface Node @deprecated(reason: "No longer supported") {
|
526
|
-
id: ID!
|
527
|
-
}
|
528
|
-
')
|
529
|
-
|
530
|
-
type = document.definitions.first
|
531
|
-
assert_equal GraphQL::Language::Nodes::InterfaceTypeDefinition, type.class
|
532
|
-
assert_equal 'Node', type.name
|
533
|
-
assert_equal 1, type.directives.length
|
534
|
-
|
535
|
-
deprecated_directive = type.directives[0]
|
536
|
-
assert_equal 'deprecated', deprecated_directive.name
|
537
|
-
assert_equal 'reason', deprecated_directive.arguments[0].name
|
538
|
-
assert_equal 'No longer supported', deprecated_directive.arguments[0].value
|
539
|
-
end
|
540
|
-
|
541
|
-
it "parses enum types" do
|
542
|
-
document = subject.parse('
|
543
|
-
enum DogCommand {
|
544
|
-
SIT
|
545
|
-
DOWN @deprecated(reason: "No longer supported")
|
546
|
-
HEEL
|
547
|
-
}
|
548
|
-
')
|
549
|
-
|
550
|
-
type = document.definitions.first
|
551
|
-
assert_equal GraphQL::Language::Nodes::EnumTypeDefinition, type.class
|
552
|
-
assert_equal 'DogCommand', type.name
|
553
|
-
assert_equal 3, type.values.length
|
554
|
-
|
555
|
-
assert_equal 'SIT', type.values[0].name
|
556
|
-
assert_equal [], type.values[0].directives
|
557
|
-
|
558
|
-
assert_equal 'DOWN', type.values[1].name
|
559
|
-
assert_equal 1, type.values[1].directives.length
|
560
|
-
deprecated_directive = type.values[1].directives[0]
|
561
|
-
assert_equal 'deprecated', deprecated_directive.name
|
562
|
-
assert_equal 'reason', deprecated_directive.arguments[0].name
|
563
|
-
assert_equal 'No longer supported', deprecated_directive.arguments[0].value
|
564
|
-
|
565
|
-
assert_equal 'HEEL', type.values[2].name
|
566
|
-
assert_equal [], type.values[2].directives
|
567
|
-
end
|
568
|
-
|
569
|
-
it "parses enum types with directives" do
|
570
|
-
document = subject.parse('
|
571
|
-
enum DogCommand @deprecated(reason: "No longer supported") {
|
572
|
-
SIT
|
573
|
-
}
|
574
|
-
')
|
575
|
-
|
576
|
-
type = document.definitions.first
|
577
|
-
assert_equal GraphQL::Language::Nodes::EnumTypeDefinition, type.class
|
578
|
-
assert_equal 'DogCommand', type.name
|
579
|
-
assert_equal 1, type.directives.length
|
580
|
-
|
581
|
-
deprecated_directive = type.directives[0]
|
582
|
-
assert_equal 'deprecated', deprecated_directive.name
|
583
|
-
assert_equal 'reason', deprecated_directive.arguments[0].name
|
584
|
-
assert_equal 'No longer supported', deprecated_directive.arguments[0].value
|
585
|
-
end
|
586
|
-
|
587
|
-
it "parses input object types" do
|
588
|
-
document = subject.parse('
|
589
|
-
input EmptyMutationInput {
|
590
|
-
clientMutationId: String
|
591
|
-
}
|
592
|
-
')
|
593
|
-
|
594
|
-
type = document.definitions.first
|
595
|
-
assert_equal GraphQL::Language::Nodes::InputObjectTypeDefinition, type.class
|
596
|
-
assert_equal 'EmptyMutationInput', type.name
|
597
|
-
assert_equal ['clientMutationId'], type.fields.map(&:name)
|
598
|
-
assert_equal 'String', type.fields[0].type.name
|
599
|
-
assert_equal nil, type.fields[0].default_value
|
600
|
-
end
|
601
|
-
|
602
|
-
it "parses input object types with directives" do
|
603
|
-
document = subject.parse('
|
604
|
-
input EmptyMutationInput @deprecated(reason: "No longer supported") {
|
605
|
-
clientMutationId: String
|
606
|
-
}
|
607
|
-
')
|
608
|
-
|
609
|
-
type = document.definitions.first
|
610
|
-
assert_equal GraphQL::Language::Nodes::InputObjectTypeDefinition, type.class
|
611
|
-
assert_equal 'EmptyMutationInput', type.name
|
612
|
-
assert_equal ['clientMutationId'], type.fields.map(&:name)
|
613
|
-
assert_equal 'String', type.fields[0].type.name
|
614
|
-
assert_equal nil, type.fields[0].default_value
|
615
|
-
assert_equal 1, type.directives.length
|
616
|
-
|
617
|
-
deprecated_directive = type.directives[0]
|
618
|
-
assert_equal 'deprecated', deprecated_directive.name
|
619
|
-
assert_equal 'reason', deprecated_directive.arguments[0].name
|
620
|
-
assert_equal 'No longer supported', deprecated_directive.arguments[0].value
|
621
|
-
|
622
|
-
end
|
623
|
-
end
|
624
|
-
end
|
625
|
-
|
626
|
-
describe "errors" do
|
627
|
-
let(:query_string) {%| query doSomething { bogus { } |}
|
628
|
-
it "raises a parse error" do
|
629
|
-
err = assert_raises(GraphQL::ParseError) { document }
|
630
|
-
end
|
631
|
-
|
632
|
-
it "correctly identifies parse error location and content" do
|
633
|
-
e = assert_raises(GraphQL::ParseError) do
|
634
|
-
GraphQL.parse("
|
635
|
-
query getCoupons {
|
636
|
-
allCoupons: {data{id}}
|
637
|
-
}
|
638
|
-
")
|
639
|
-
end
|
640
|
-
assert_includes(e.message, '"{"')
|
641
|
-
assert_includes(e.message, "LCURLY")
|
642
|
-
assert_equal(3, e.line)
|
643
|
-
assert_equal(33, e.col)
|
644
|
-
end
|
645
|
-
|
646
|
-
it "handles unexpected ends" do
|
647
|
-
err = assert_raises(GraphQL::ParseError) { GraphQL.parse("{ ") }
|
648
|
-
assert_equal "Unexpected end of document", err.message
|
649
|
-
end
|
650
|
-
|
651
|
-
it "rejects unsupported characters" do
|
652
|
-
e = assert_raises(GraphQL::ParseError) do
|
653
|
-
GraphQL.parse("{ field; }")
|
654
|
-
end
|
655
|
-
|
656
|
-
assert_includes(e.message, "Parse error on \";\"")
|
657
|
-
end
|
658
|
-
|
659
|
-
it "rejects control characters" do
|
660
|
-
e = assert_raises(GraphQL::ParseError) do
|
661
|
-
GraphQL.parse("{ \afield }")
|
662
|
-
end
|
663
|
-
|
664
|
-
assert_includes(e.message, "Parse error on \"\\a\"")
|
665
|
-
end
|
666
|
-
|
667
|
-
it "rejects partial BOM" do
|
668
|
-
e = assert_raises(GraphQL::ParseError) do
|
669
|
-
GraphQL.parse("{ \xeffield }")
|
670
|
-
end
|
671
|
-
|
672
|
-
assert_includes(e.message, "Parse error on \"\\xEF\"")
|
673
|
-
end
|
674
|
-
|
675
|
-
it "rejects vertical tabs" do
|
676
|
-
e = assert_raises(GraphQL::ParseError) do
|
677
|
-
GraphQL.parse("{ \vfield }")
|
678
|
-
end
|
679
|
-
|
680
|
-
assert_includes(e.message, "Parse error on \"\\v\"")
|
681
|
-
end
|
682
|
-
|
683
|
-
it "rejects form feed" do
|
684
|
-
e = assert_raises(GraphQL::ParseError) do
|
685
|
-
GraphQL.parse("{ \ffield }")
|
686
|
-
end
|
687
|
-
|
688
|
-
assert_includes(e.message, "Parse error on \"\\f\"")
|
689
|
-
end
|
690
|
-
|
691
|
-
it "rejects no break space" do
|
692
|
-
e = assert_raises(GraphQL::ParseError) do
|
693
|
-
GraphQL.parse("{ \xa0field }")
|
694
|
-
end
|
695
|
-
|
696
|
-
assert_includes(e.message, "Parse error on \"\\xA0\"")
|
697
|
-
end
|
698
|
-
|
699
|
-
it "rejects unterminated strings" do
|
700
|
-
e = assert_raises(GraphQL::ParseError) do
|
701
|
-
GraphQL.parse("\"")
|
702
|
-
end
|
703
|
-
|
704
|
-
assert_includes(e.message, "Parse error on \"\\\"\"")
|
705
|
-
|
706
|
-
e = assert_raises(GraphQL::ParseError) do
|
707
|
-
GraphQL.parse("\"\n\"")
|
708
|
-
end
|
709
|
-
|
710
|
-
assert_includes(e.message, "Parse error on \"\\n\"")
|
711
|
-
end
|
712
|
-
|
713
|
-
it "rejects bad escape sequence in strings" do
|
714
|
-
e = assert_raises(GraphQL::ParseError) do
|
715
|
-
GraphQL.parse("{ field(arg:\"\\x\") }")
|
716
|
-
end
|
717
|
-
|
718
|
-
assert_includes(e.message, "Parse error on bad Unicode escape sequence")
|
719
|
-
end
|
720
|
-
|
721
|
-
it "rejects incomplete escape sequence in strings" do
|
722
|
-
e = assert_raises(GraphQL::ParseError) do
|
723
|
-
GraphQL.parse("{ field(arg:\"\\u1\") }")
|
724
|
-
end
|
725
|
-
|
726
|
-
assert_includes(e.message, "bad Unicode escape sequence")
|
727
|
-
end
|
728
|
-
|
729
|
-
it "rejects unicode escape with bad chars" do
|
730
|
-
e = assert_raises(GraphQL::ParseError) do
|
731
|
-
GraphQL.parse("{ field(arg:\"\\u0XX1\") }")
|
732
|
-
end
|
733
|
-
|
734
|
-
assert_includes(e.message, "bad Unicode escape sequence")
|
735
|
-
|
736
|
-
e = assert_raises(GraphQL::ParseError) do
|
737
|
-
GraphQL.parse("{ field(arg:\"\\uXXXX\") }")
|
738
|
-
end
|
739
|
-
|
740
|
-
assert_includes(e.message, "bad Unicode escape sequence")
|
741
|
-
|
742
|
-
|
743
|
-
e = assert_raises(GraphQL::ParseError) do
|
744
|
-
GraphQL.parse("{ field(arg:\"\\uFXXX\") }")
|
745
|
-
end
|
746
|
-
|
747
|
-
assert_includes(e.message, "bad Unicode escape sequence")
|
748
|
-
|
749
|
-
|
750
|
-
e = assert_raises(GraphQL::ParseError) do
|
751
|
-
GraphQL.parse("{ field(arg:\"\\uXXXF\") }")
|
752
|
-
end
|
753
|
-
|
754
|
-
assert_includes(e.message, "bad Unicode escape sequence")
|
755
|
-
end
|
756
|
-
|
757
|
-
it "rejects fragments named 'on'" do
|
758
|
-
e = assert_raises(GraphQL::ParseError) do
|
759
|
-
GraphQL.parse("fragment on on on { on }")
|
760
|
-
end
|
761
|
-
|
762
|
-
assert_includes(e.message, "Parse error on \"on\"")
|
763
|
-
end
|
764
|
-
|
765
|
-
it "rejects fragment spread of 'on'" do
|
766
|
-
e = assert_raises(GraphQL::ParseError) do
|
767
|
-
GraphQL.parse("{ ...on }")
|
768
|
-
end
|
769
|
-
|
770
|
-
assert_includes(e.message, "Parse error on \"}\"")
|
771
|
-
end
|
772
|
-
|
773
|
-
it "rejects null value" do
|
774
|
-
e = assert_raises(GraphQL::ParseError) do
|
775
|
-
GraphQL.parse("{ fieldWithNullableStringInput(input: null) }")
|
776
|
-
end
|
777
|
-
|
778
|
-
assert_includes(e.message, "Parse error on \"null\"")
|
779
|
-
end
|
780
|
-
end
|
781
|
-
|
782
|
-
|
783
|
-
describe "whitespace" do
|
784
|
-
describe "whitespace-only queries" do
|
785
|
-
let(:query_string) { " " }
|
786
|
-
it "doesn't blow up" do
|
787
|
-
assert_equal [], document.definitions
|
788
|
-
end
|
789
|
-
end
|
790
|
-
|
791
|
-
describe "empty string queries" do
|
792
|
-
let(:query_string) { "" }
|
793
|
-
it "doesn't blow up" do
|
794
|
-
assert_equal [], document.definitions
|
795
|
-
end
|
796
|
-
end
|
797
|
-
|
798
|
-
describe "using tabs as whitespace" do
|
799
|
-
let(:query_string) { "\t{\t\tid, \tname}"}
|
800
|
-
it "parses the query" do
|
801
|
-
assert_equal 1, document.definitions.length
|
802
|
-
end
|
803
|
-
end
|
804
|
-
end
|
805
|
-
end
|
806
|
-
end
|
807
|
-
end
|
808
|
-
end
|
809
|
-
end
|