rspec-graphql_response 0.1.0 → 0.4.1
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/.github/workflows/test.yml +14 -0
- data/.gitignore +3 -0
- data/.gituprc +128 -0
- data/Gemfile.lock +16 -7
- data/README.md +64 -72
- data/RELEASE_NOTES.md +45 -0
- data/UPGRADE.md +41 -0
- data/docs/add_helper.md +94 -0
- data/docs/add_validator.md +168 -1
- data/docs/configuration.md +12 -0
- data/docs/execute_graphql.md +50 -0
- data/docs/graphql_spec_type.md +14 -0
- data/docs/have_operation.md +46 -0
- data/docs/operation.md +1 -36
- data/docs/response.md +1 -0
- data/docs/response_data.md +146 -0
- data/lib/rspec/graphql_response.rb +2 -1
- data/lib/rspec/graphql_response/dig_dug/dig_dug.rb +83 -0
- data/lib/rspec/graphql_response/helpers.rb +35 -7
- data/lib/rspec/graphql_response/helpers/execute_graphql.rb +14 -1
- data/lib/rspec/graphql_response/helpers/graphql_context.rb +3 -0
- data/lib/rspec/graphql_response/helpers/graphql_operation.rb +3 -0
- data/lib/rspec/graphql_response/helpers/graphql_variables.rb +3 -0
- data/lib/rspec/graphql_response/helpers/operation.rb +1 -0
- data/lib/rspec/graphql_response/helpers/response_data.rb +13 -0
- data/lib/rspec/graphql_response/matchers.rb +1 -0
- data/lib/rspec/graphql_response/matchers/have_errors.rb +2 -2
- data/lib/rspec/graphql_response/matchers/have_operation.rb +23 -0
- data/lib/rspec/graphql_response/validators.rb +6 -0
- data/lib/rspec/graphql_response/validators/have_operation.rb +24 -0
- data/lib/rspec/graphql_response/version.rb +1 -1
- data/rspec-graphql_response.gemspec +3 -2
- metadata +52 -21
- data/.travis.yml +0 -7
data/UPGRADE.md
ADDED
@@ -0,0 +1,41 @@
|
|
1
|
+
# Upgrade Guide
|
2
|
+
|
3
|
+
## v0.3.0 to v0.4.0
|
4
|
+
|
5
|
+
There is a breaking change between v0.3.0 and v0.4.0 where the helper `graphql_query` was renamed to `graphql_operation`. In order to migrate to v0.4.0 all references to `graphql_query` can be replaced with `graphql_operation`.
|
6
|
+
|
7
|
+
## v0.1.0 to v0.2.0
|
8
|
+
|
9
|
+
There is a breaking change between v0.1.0 and v0.2.0 regarding the configuration of graphql queries, variables and context.
|
10
|
+
Previously, you defined these three items with `let` in rspec:
|
11
|
+
|
12
|
+
```ruby
|
13
|
+
let(:graphql_query) { ... }
|
14
|
+
```
|
15
|
+
|
16
|
+
In v0.2.0, this changes to a new DSL for each of these three items:
|
17
|
+
|
18
|
+
```ruby
|
19
|
+
RSpec.describe My::Stuff, type: :graphql do
|
20
|
+
|
21
|
+
graphql_query <<-GQL
|
22
|
+
query Characters($name: String) {
|
23
|
+
characters(name: $name) {
|
24
|
+
id
|
25
|
+
name
|
26
|
+
}
|
27
|
+
}
|
28
|
+
GQL
|
29
|
+
|
30
|
+
graphql_variables({
|
31
|
+
name: "Jam"
|
32
|
+
})
|
33
|
+
|
34
|
+
graphql_context({
|
35
|
+
some: "context here",
|
36
|
+
current_user: some_user
|
37
|
+
})
|
38
|
+
|
39
|
+
it " ... "
|
40
|
+
end
|
41
|
+
```
|
data/docs/add_helper.md
ADDED
@@ -0,0 +1,94 @@
|
|
1
|
+
# Adding Helper Methods with `.add_helper`
|
2
|
+
|
3
|
+
Much of the value that RSpec::GraphQLResponse brings is in the form of helper methods. Some helpers
|
4
|
+
will let you simplify a spec from a verbose set of structure parsing, like this:
|
5
|
+
|
6
|
+
```ruby
|
7
|
+
it "does stuff" do
|
8
|
+
characters = response["data"]["characters"]
|
9
|
+
|
10
|
+
expect(characters).to include(
|
11
|
+
...
|
12
|
+
)
|
13
|
+
end
|
14
|
+
```
|
15
|
+
|
16
|
+
Down into something a little more reasonable with the `operation` helper, like this:
|
17
|
+
|
18
|
+
```ruby
|
19
|
+
it "does stuff" do
|
20
|
+
expect(operation(:characters)).to include(
|
21
|
+
...
|
22
|
+
)
|
23
|
+
end
|
24
|
+
```
|
25
|
+
|
26
|
+
It's not a huge difference, but when you consider how well the `operation` method handles `nil` and
|
27
|
+
operation results that are not found, it's well worth the few lines of savings.
|
28
|
+
|
29
|
+
There are many other helpers available for your test suites, as well. Be sure to check the full
|
30
|
+
documentation list in the main readme.
|
31
|
+
|
32
|
+
## Add a Spec Helper
|
33
|
+
|
34
|
+
Most of the helpers that you'll want to add will be methods that are made available inside of
|
35
|
+
your `it` (and related / similar) blocks, as shown above.
|
36
|
+
|
37
|
+
To do this, you only need to call `RSpec::GraphQLResponse.add_helper(name, &helper_block)`. The `name` should
|
38
|
+
be a symbol that represents the method name you wish to create. And the `&helper_block` is the actual
|
39
|
+
method body, with any params you need.
|
40
|
+
|
41
|
+
Within the helper body, you will have access to all available features of RSpec's `it` blocks, including
|
42
|
+
other helper methods that come with RSpec::GraphQLResponse.
|
43
|
+
|
44
|
+
A simple example can be found in the `operation` helper:
|
45
|
+
|
46
|
+
```ruby
|
47
|
+
RSpec::GraphQLResponse.add_helper :operation do |operation_name|
|
48
|
+
return nil unless response.is_a? Hash
|
49
|
+
|
50
|
+
response.dig("data", operation_name)
|
51
|
+
end
|
52
|
+
```
|
53
|
+
|
54
|
+
In this example, the `response` helper is used, which guarantees the graphql has been executed and a response
|
55
|
+
is available, assuming there were no exceptions preventing that.
|
56
|
+
|
57
|
+
## Add a Context Helper
|
58
|
+
|
59
|
+
In addition to Spec level helpers, RSpec::GraphQLResponse allows you to add custom helpers at the context
|
60
|
+
level. This means you can add configuration and other bits that can be called outside of an `it` block.
|
61
|
+
The existing `graphql_operation` and other DSL methods for configuring graphql calls are a great example of context level helpers.
|
62
|
+
|
63
|
+
To create a context helper, call `RSpec::GraphQLResponse.add_context_helper(name, &helper_block)`. The params
|
64
|
+
are the same as `.add_helper`, but the resulting method will be made available in `describe` and `context`
|
65
|
+
blocks.
|
66
|
+
|
67
|
+
```ruby
|
68
|
+
RSpec::GraphQLResponse.add_context_helper :my_setting do |some_arg|
|
69
|
+
@my_setting = some_arg
|
70
|
+
end
|
71
|
+
```
|
72
|
+
|
73
|
+
In this simple example, a method called `my_setting` is created, and it stores a value in the instance variable
|
74
|
+
`@my_setting`. This takes advantage of RSpec's native ability to handle instance variables in describe and
|
75
|
+
context blocks, allowing the variable to exist withing the hierarchy of objects for a given spec.
|
76
|
+
|
77
|
+
With that defined, it can be used within a spec:
|
78
|
+
|
79
|
+
```ruby
|
80
|
+
RSpec.describe Cool::Thing, type: :graphql do
|
81
|
+
|
82
|
+
my_setting "this is a cool setting!"
|
83
|
+
|
84
|
+
|
85
|
+
it "has the setting" do
|
86
|
+
setting = self.class.instance_variable_get(:@my_setting)
|
87
|
+
|
88
|
+
expect(setting).to eq("this is a cool setting!")
|
89
|
+
end
|
90
|
+
end
|
91
|
+
```
|
92
|
+
|
93
|
+
You may want to provide a better way of retrieving the `@my_setting` var from the class, but this at least
|
94
|
+
demonstrates the ability to retrieve it from the class created by the `describe` block.
|
data/docs/add_validator.md
CHANGED
@@ -6,5 +6,172 @@ organizing your matcher code.
|
|
6
6
|
|
7
7
|
To solve this, RSpec::GraphQLResponse introduced validators - code that is
|
8
8
|
called to manage the validation of a graphql response, and provide the failure
|
9
|
-
messages.
|
9
|
+
messages.
|
10
|
+
|
11
|
+
|
12
|
+
## Clean Matchers with Validators
|
13
|
+
|
14
|
+
Validators allow your code to be well organized, and unit tested,
|
10
15
|
while providing a clean implementation of the matcher, as shown here:
|
16
|
+
|
17
|
+
```ruby
|
18
|
+
RSpec::GraphQLResponse.add_matcher :foo_bar do |expected_value)
|
19
|
+
match do |actual_value|
|
20
|
+
foo_bar = RSpec::GraphQLResponse.validator(:foo_bar)
|
21
|
+
|
22
|
+
@result = foo_bar.validate(actual_value, expected_value)
|
23
|
+
end
|
24
|
+
|
25
|
+
failure_message do |response|
|
26
|
+
@result.reason
|
27
|
+
end
|
28
|
+
end
|
29
|
+
```
|
30
|
+
|
31
|
+
## Add a Validator with a Failure Message
|
32
|
+
|
33
|
+
```ruby
|
34
|
+
RSpec::GraphQLResponse.add_validator :foo_bar do
|
35
|
+
# a regular failure message named `:nil`
|
36
|
+
failure_message :nil, "Can't validate nil"
|
37
|
+
|
38
|
+
# a lambda failure message named `:not_foo`
|
39
|
+
# this lets you pass data to the message so it can
|
40
|
+
# be more expressive of the failure reason
|
41
|
+
failure_message :not_foo, -> (actual, expected) { "Expected it to #{actual}, but it did not. found #{expected}" }
|
42
|
+
|
43
|
+
validate do |actual, expected|
|
44
|
+
# fail for one reason
|
45
|
+
next fail_validation(:nil) if actual.nil?
|
46
|
+
|
47
|
+
# fail for another reason, with data passed
|
48
|
+
# to the failure message
|
49
|
+
next fail_validation(:not_foo, actual, expected) unless actual == expected
|
50
|
+
|
51
|
+
# no failures found, so pass it!
|
52
|
+
pass_validation
|
53
|
+
end
|
54
|
+
end
|
55
|
+
```
|
56
|
+
|
57
|
+
### Method: `failure_message(name, message)`
|
58
|
+
|
59
|
+
The `failure_message` method allows a message to be registered with a specific name and a message associated with that name.
|
60
|
+
|
61
|
+
`failure_message name, message`
|
62
|
+
|
63
|
+
The `name` should be symbol which represents a reason for failure.
|
64
|
+
|
65
|
+
The `message` can either be a string containing the failure message, or a lambda (proc) that returns a string with the message.
|
66
|
+
|
67
|
+
```ruby
|
68
|
+
RSpec::GraphQLResponse.add_validator :some_validator do
|
69
|
+
failure_message :a_failure, "Failed because of a failure"
|
70
|
+
|
71
|
+
# ...
|
72
|
+
end
|
73
|
+
```
|
74
|
+
|
75
|
+
Providing a lambda for the message allows the message to receive parameters and format the message using those parameters.
|
76
|
+
|
77
|
+
```ruby
|
78
|
+
RSpec::GraphQLResponse.add_validator :some_validator do
|
79
|
+
failure_message :wrong_value, ->(value) { "Failed because #{value} is not 'foo'" }
|
80
|
+
|
81
|
+
# ...
|
82
|
+
end
|
83
|
+
```
|
84
|
+
|
85
|
+
The failure messages can be referenced in the validation methods by calling `fail_validation` (see below)
|
86
|
+
|
87
|
+
### Method: `validate(&validation_block)`
|
88
|
+
|
89
|
+
Provide a validation method to determine if the actual value meets expectations. This validation method is most commonly
|
90
|
+
used with the `expect(something).to ...` RSpec matcher syntax.
|
91
|
+
|
92
|
+
The `&validation_block` will receive the actual value to validate as well as any other parameters that may need to be defined.
|
93
|
+
There are no limitations on the number of parameters or their names.
|
94
|
+
|
95
|
+
```ruby
|
96
|
+
RSpec::GraphQLResponse.add_validator :some_validator do
|
97
|
+
# ... failure_messages and other bits
|
98
|
+
|
99
|
+
validate do |actual, some_value|
|
100
|
+
# ... validation logic here
|
101
|
+
|
102
|
+
next fail_validation(:reason) if some_failure_criteria
|
103
|
+
end
|
104
|
+
end
|
105
|
+
```
|
106
|
+
|
107
|
+
### Method: `validate_negated(&validation_block)`
|
108
|
+
|
109
|
+
Provide a negated validation method to determine if the actual value meets expectations. This validation method is
|
110
|
+
most commonly used with the negated expecation syntax, such as `expect(something).to_not ...`.
|
111
|
+
|
112
|
+
The `&validation_block` will receive the actual value to validate as well as any other parameters that may need to be defined.
|
113
|
+
There are no limitations on the number of parameters or their names.
|
114
|
+
|
115
|
+
```ruby
|
116
|
+
RSpec::GraphQLResponse.add_validator :some_validator do
|
117
|
+
# ... failure_messages and other bits
|
118
|
+
|
119
|
+
validate_negated do |actual, some_value|
|
120
|
+
# ... negated validation logic here
|
121
|
+
|
122
|
+
next fail_validation(:reason) unless some_failure_criteria
|
123
|
+
end
|
124
|
+
end
|
125
|
+
```
|
126
|
+
|
127
|
+
Remember that `negated` validation should prove that the specified condition does _not_ exist. For example, when writing
|
128
|
+
an expectation such as `expect(response).to_not have_errors`, the `validate_negated` method needs to verify that there are
|
129
|
+
no errors in the response.
|
130
|
+
|
131
|
+
```ruby
|
132
|
+
validate_negated do |response|
|
133
|
+
errors = response.fetch("errors", [])
|
134
|
+
|
135
|
+
# check for the negation of have_errors, meaning check to
|
136
|
+
# make sure there are no errors
|
137
|
+
next fail_validation(:found_errors) unless errors.count == 0
|
138
|
+
end
|
139
|
+
```
|
140
|
+
|
141
|
+
### Validation Failure
|
142
|
+
|
143
|
+
When the actual value does not reflect any expected value, a failure can be noted by calling `next failure_message(name, [values])`.
|
144
|
+
|
145
|
+
The `name` is a symbol which has been registered using the `failure_message` method (see above). And `[values]` are an optional
|
146
|
+
array of values that will be provided to the failure message if the registered message is a lambda (proc).
|
147
|
+
|
148
|
+
```ruby
|
149
|
+
vadliate do |actual|
|
150
|
+
|
151
|
+
# something went wrong, better fail!
|
152
|
+
next fail_validation(:bad_thing) if actual.bad_value
|
153
|
+
end
|
154
|
+
```
|
155
|
+
|
156
|
+
**IMPORTANT NOTE**
|
157
|
+
|
158
|
+
There can be only _one_ valid reason for failure at a time. Therefore, as soon as a failure condition is met, the `fail_validation`
|
159
|
+
method should be invoked. Then to prevent the rest of the method from executing, the `validate` or `validate_negates` method should
|
160
|
+
be halted immediately. Due to the syntax requirements of ruby lambdas and procs, this is done with the `next` keyword.
|
161
|
+
|
162
|
+
So be sure to call `next failure_message ...` and not just `failure_message`!
|
163
|
+
|
164
|
+
### Validation Success
|
165
|
+
|
166
|
+
If all validation checks pass, the final call in the `validate` or `validate_negated` methods should be `pass_validation`
|
167
|
+
|
168
|
+
```ruby
|
169
|
+
validate do |actual|
|
170
|
+
# ... check for failed conditions
|
171
|
+
|
172
|
+
# everything passed!
|
173
|
+
pass_validation
|
174
|
+
end
|
175
|
+
```
|
176
|
+
|
177
|
+
The `pass_validation` method takes in no parameters, as a valid result does not provide a reason.
|
@@ -0,0 +1,12 @@
|
|
1
|
+
# RSpec::GraphQLResponse Configuration
|
2
|
+
|
3
|
+
To get things rolling, add a configuration block to your `spec_helper.rb` (or other file that gets included in specs, as
|
4
|
+
desired). Within this block, you'll need to provide the GraphQL Schema to use for query execution.
|
5
|
+
|
6
|
+
```ruby
|
7
|
+
RSpec::GraphQLResponse.configure |config| do
|
8
|
+
|
9
|
+
config.graphql_schema = MyGraphQLSchema
|
10
|
+
|
11
|
+
end
|
12
|
+
```
|
@@ -0,0 +1,50 @@
|
|
1
|
+
# Executing GraphQL with Helper Method `execute_graphql`
|
2
|
+
|
3
|
+
Simplifies execution of a graphql query, using `context` / `describe` level helper
|
4
|
+
methods for configuring the query.
|
5
|
+
|
6
|
+
```ruby
|
7
|
+
RSPec.describe Cool::Stuff, type: :graphql do
|
8
|
+
let(:user) { create(:graphql_user) }
|
9
|
+
let(:search_name) { "Pet" }
|
10
|
+
|
11
|
+
graphql_operation <<-GQL
|
12
|
+
query SomeThing($name: String) {
|
13
|
+
characters(name: $name) {
|
14
|
+
id
|
15
|
+
name
|
16
|
+
}
|
17
|
+
}
|
18
|
+
GQL
|
19
|
+
|
20
|
+
graphql_variables do
|
21
|
+
{
|
22
|
+
name: search_name
|
23
|
+
}
|
24
|
+
end
|
25
|
+
|
26
|
+
grapql_context do
|
27
|
+
{
|
28
|
+
current_user: user
|
29
|
+
}
|
30
|
+
end
|
31
|
+
|
32
|
+
it "executes and does the thing with the vars and context" do
|
33
|
+
# ... expect things here
|
34
|
+
end
|
35
|
+
end
|
36
|
+
```
|
37
|
+
|
38
|
+
## Available Configuration Methods
|
39
|
+
|
40
|
+
### `graphql_operation`
|
41
|
+
|
42
|
+
A string - most commonly a ruby heredoc - for the graphql query to execute
|
43
|
+
|
44
|
+
### `graphql_variables`
|
45
|
+
|
46
|
+
A hash of keys and values that the query expects to use
|
47
|
+
|
48
|
+
### `graphql_context`
|
49
|
+
|
50
|
+
A hash of keys and values that are passed to a query or mutation, as `context`, in that query or mutation's resolver method.
|
@@ -0,0 +1,14 @@
|
|
1
|
+
# Custom Spec Type, `type: :grapql`
|
2
|
+
|
3
|
+
To use the custom helpers and matchers provide by RSpec::GraphQLResponse, you need to specificy `type: :graphql` as
|
4
|
+
part of your spec's description.
|
5
|
+
|
6
|
+
```ruby
|
7
|
+
RSpec.describe My::Cool::Thing, type: :graphql do
|
8
|
+
|
9
|
+
# ... all of RSpec::GraphQLResponse is now available!
|
10
|
+
|
11
|
+
end
|
12
|
+
```
|
13
|
+
|
14
|
+
With this `type` set, your specs will have access to all of the `RSpec::GraphQLResponse` features.
|
@@ -0,0 +1,46 @@
|
|
1
|
+
# Validate a response operation with `have_operation(name)`
|
2
|
+
|
3
|
+
Check for the presence of an operation's results. Useful when you want to ensure an operation exists before
|
4
|
+
retrieving the operation's results.
|
5
|
+
|
6
|
+
## Validate an Operation is Present
|
7
|
+
|
8
|
+
```ruby
|
9
|
+
RSpec.describe My::Characters, type: :graphql do
|
10
|
+
graphql_operation <<-GQL
|
11
|
+
query CharacterList {
|
12
|
+
characters {
|
13
|
+
id
|
14
|
+
name
|
15
|
+
}
|
16
|
+
}
|
17
|
+
GQL
|
18
|
+
|
19
|
+
it "has the characters" do
|
20
|
+
|
21
|
+
expect(response).to have_operation(:characters)
|
22
|
+
|
23
|
+
end
|
24
|
+
end
|
25
|
+
```
|
26
|
+
|
27
|
+
## Validate an operation is NOT Present
|
28
|
+
|
29
|
+
```ruby
|
30
|
+
RSpec.describe My::Characters, type: :graphql do
|
31
|
+
graphql_operation <<-GQL
|
32
|
+
query CharacterList {
|
33
|
+
characters {
|
34
|
+
id
|
35
|
+
name
|
36
|
+
}
|
37
|
+
}
|
38
|
+
GQL
|
39
|
+
|
40
|
+
it "does not have books" do
|
41
|
+
|
42
|
+
expect(response).to_not have_operation(:books)
|
43
|
+
|
44
|
+
end
|
45
|
+
end
|
46
|
+
```
|
data/docs/operation.md
CHANGED
@@ -1,38 +1,3 @@
|
|
1
1
|
# Using the `operation` helper
|
2
2
|
|
3
|
-
|
4
|
-
structure that looks like,
|
5
|
-
|
6
|
-
```ruby
|
7
|
-
{
|
8
|
-
"data" => {
|
9
|
-
operation_name
|
10
|
-
}
|
11
|
-
}
|
12
|
-
```
|
13
|
-
|
14
|
-
## Basic Use
|
15
|
-
|
16
|
-
```ruby
|
17
|
-
it "has characters" do
|
18
|
-
characters = operation(:characters)
|
19
|
-
|
20
|
-
expect(character).to include(
|
21
|
-
{ id: 1, name: "Jam" },
|
22
|
-
# ...
|
23
|
-
)
|
24
|
-
end
|
25
|
-
```
|
26
|
-
|
27
|
-
## Handling Nil
|
28
|
-
|
29
|
-
If there is no `"data"` or no named operation for the name supplied, the
|
30
|
-
`operation` helper will return `nil`
|
31
|
-
|
32
|
-
```ruby
|
33
|
-
it "returns nil if operation doesn't exist" do
|
34
|
-
character = operation(:something_that_does_not_exist)
|
35
|
-
|
36
|
-
expect(operation).to be_nil
|
37
|
-
end
|
38
|
-
```
|
3
|
+
Deprecated. See [response_data](response_data.md) instead.
|