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.
Files changed (35) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/test.yml +14 -0
  3. data/.gitignore +3 -0
  4. data/.gituprc +128 -0
  5. data/Gemfile.lock +16 -7
  6. data/README.md +64 -72
  7. data/RELEASE_NOTES.md +45 -0
  8. data/UPGRADE.md +41 -0
  9. data/docs/add_helper.md +94 -0
  10. data/docs/add_validator.md +168 -1
  11. data/docs/configuration.md +12 -0
  12. data/docs/execute_graphql.md +50 -0
  13. data/docs/graphql_spec_type.md +14 -0
  14. data/docs/have_operation.md +46 -0
  15. data/docs/operation.md +1 -36
  16. data/docs/response.md +1 -0
  17. data/docs/response_data.md +146 -0
  18. data/lib/rspec/graphql_response.rb +2 -1
  19. data/lib/rspec/graphql_response/dig_dug/dig_dug.rb +83 -0
  20. data/lib/rspec/graphql_response/helpers.rb +35 -7
  21. data/lib/rspec/graphql_response/helpers/execute_graphql.rb +14 -1
  22. data/lib/rspec/graphql_response/helpers/graphql_context.rb +3 -0
  23. data/lib/rspec/graphql_response/helpers/graphql_operation.rb +3 -0
  24. data/lib/rspec/graphql_response/helpers/graphql_variables.rb +3 -0
  25. data/lib/rspec/graphql_response/helpers/operation.rb +1 -0
  26. data/lib/rspec/graphql_response/helpers/response_data.rb +13 -0
  27. data/lib/rspec/graphql_response/matchers.rb +1 -0
  28. data/lib/rspec/graphql_response/matchers/have_errors.rb +2 -2
  29. data/lib/rspec/graphql_response/matchers/have_operation.rb +23 -0
  30. data/lib/rspec/graphql_response/validators.rb +6 -0
  31. data/lib/rspec/graphql_response/validators/have_operation.rb +24 -0
  32. data/lib/rspec/graphql_response/version.rb +1 -1
  33. data/rspec-graphql_response.gemspec +3 -2
  34. metadata +52 -21
  35. 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
+ ```
@@ -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.
@@ -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. Validators allow your code to be well organized, and unit tested,
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
- The `operation` helper will dig through a response to find a data
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.