rspec-graphql_response 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 67d1e0d86474c6b5f5e99c3d19e3d1645fe35819d5a47a4fd7007af9c9a5700e
4
- data.tar.gz: d27339cca95c74fdb174fa0ca6a43116039b449e0b22ef6397ccd76e8eb472d1
3
+ metadata.gz: 352fd084b9379469d7b4f71c86cfe94ea90172047c61c1ea2d20dced4bc85026
4
+ data.tar.gz: 7eccaeb870a708fe43f2b0891a45d703b7d1fd64901efddd5323a27b6d87a77f
5
5
  SHA512:
6
- metadata.gz: b427ec094b4a8e19865e3d610a14abb32625189f7ea190ca61dbcde61e8582abca4464980e2790bb2d30b8ecbb83912034f4a95bcc1cd8744410328a646ad755
7
- data.tar.gz: d0864628a5e6beb9096fe8901f67dd6f75d824af26631e1b66ea97894bd30da4061f6ceb25ffc692461a1e54a50f7e7c56405f89cce6275e919d624c9dd29dc8
6
+ metadata.gz: 83ebc95cd8c33beb6f62df67c9eb7a6846f820ca7bbc2c8412aadd0b4ceb0098449c23aeaf599bcaf65db6ddd4f4d92d2029a46e5a83c2b463f26e826040156c
7
+ data.tar.gz: 876549c88363f4e330bdb01416940116bb7a997ee7f22d2864a0d8c7a55b9310df7198b135d4bc1d655b22adecfc02df09e89feb7e173f5febfd4f2a0bd73f9a
@@ -0,0 +1,14 @@
1
+ name: Test rspec-graphql_response
2
+ on:
3
+ push:
4
+ branches: [ $default-branch ]
5
+ pull_request:
6
+ jobs:
7
+ test:
8
+ runs-on: ubuntu-latest
9
+ steps:
10
+ - uses: actions/checkout@v2
11
+ - uses: ruby/setup-ruby@v1
12
+ with:
13
+ bundler-cache: true
14
+ - run: bin/rspec
data/.gitignore CHANGED
@@ -9,3 +9,6 @@
9
9
 
10
10
  # rspec failure tracking
11
11
  .rspec_status
12
+
13
+ # ignore the resulting .gem file from gem build
14
+ rspec-graphql_response*.gem
data/.gituprc ADDED
@@ -0,0 +1,128 @@
1
+ # Gitup: Git Update Dance Configuration
2
+ # -------------------------------------
3
+ # configuration for gitup can be set as a default for the current user
4
+ # by adding a ~/.gituprc file with settings
5
+ #
6
+ # current project or local directory settings can be added in ./.gituprc
7
+ #
8
+ # the .gituprc file is a valid shell script, loaded every time gitup is
9
+ # run. this means you can add logic, functions, and other calls to the
10
+ # .gituprc file and it will be executed at runtime
11
+
12
+
13
+ # GIT CONFIGURATION
14
+ # -----------------
15
+ # Manages the git branch update process for your project
16
+ #
17
+ # GITUP_SKIP_UPDATE:
18
+ # Whether or not a git update is performed prior to running
19
+ # the bundle install or db:migrate steps
20
+ #
21
+ # Options:
22
+ # 0: (default) do not skip the update
23
+ # 1: skip the update
24
+ #
25
+ # GITUP_MERGE_COMMAND:
26
+ # How to update the local git branch, from the upstream branch.
27
+ #
28
+ # Options:
29
+ # rebase: (default) force a rebase of the upstream branch
30
+ # merge: force a merge of the upstream branch
31
+ # <other>: your own command: `git <other> <remote>/<branch>`
32
+ #
33
+ # GITUP_REMOTE_NAME:
34
+ # The git remote name to pull from
35
+ #
36
+ # Default:
37
+ # origin
38
+ #
39
+ # GITUP_BRANCH_NAME:
40
+ # The main remote branch name to pull, from which your current
41
+ # branch will be updated
42
+ #
43
+ # Default:
44
+ # development
45
+ #
46
+ # GITUP_GIT_UPDATE_FN:
47
+ # The method called to update your git branch. Responsible
48
+ # for handling git pull, git merge or rebase, etc.
49
+ #
50
+ # Params:
51
+ # $1: Merge Command
52
+ # $2: Remote Name
53
+ # $3: Branch Name
54
+ #
55
+ # Default:
56
+ # GITUP_GIT_UPDATE_FN=__gitup_git_update
57
+ #
58
+ GITUP_SKIP_UPDATE=0
59
+ # GITUP_MERGE_COMMAND=rebase
60
+ GITUP_REMOTE_NAME=origin
61
+ GITUP_BRANCH_NAME=main
62
+ GITUP_GIT_UPDATE_FN=__gitup_git_update
63
+
64
+ # ADVANCED GIT CONFIGURATION
65
+ # --------------------------
66
+ # This .gituprs file is a full bash script. You can add logic
67
+ # to determine how to update, based on your current branch, etc.
68
+ #
69
+ # Examples:
70
+ #
71
+ # How to change merge command for specific branches
72
+ #
73
+ # current git branch
74
+ local branch_name=$(git symbolic-ref -q HEAD)
75
+
76
+ # remove the `refs/heads/` structure from the name
77
+ branch_name=${branch_name##refs/heads/}
78
+
79
+ # merge if we're in 'development' branch
80
+ if [[ $branch_name == $GITUP_BRANCH_NAME ]]; then
81
+ GITUP_MERGE_COMMAND=merge
82
+ else
83
+ GITUP_MERGE_COMMAND=rebase
84
+ fi
85
+
86
+ # DEPENDENCY INSTALLATION
87
+ # -----------------------
88
+ # Manage the step installs dependencies, after updating git.
89
+ # For example, run `bundle install` (default) and/or other
90
+ # commands to keep your project's dependencies up to date.
91
+ #
92
+ # GITUP_SKIP_INSTALL_DEPENDENCIES:
93
+ # Whether or not to skip the step that runs immediately after
94
+ # the git-update completes
95
+ #
96
+ # Options:
97
+ # 0: (default) do not skip the after_update step
98
+ # 1: skip the after_update step
99
+ #
100
+ # GITUP_INSTALL_DEPENDENCIES_FN:
101
+ # Function to run after gitup has completed the git update process
102
+ #
103
+ # Default:
104
+ # GITUP_INSTALL_DEPENDENCIES_FN=__gitup_install_dependencies
105
+ #
106
+ GITUP_SKIP_INSTALL_DEPENDENCIES=0
107
+ GITUP_INSTALL_DEPENDENCIES_FN=__gitup_install_dependencies
108
+
109
+ # RUN MIGRATIONS
110
+ # --------------
111
+ # Manage the process of running schema and/or data migrations,
112
+ # after your project's post-git-update function has run
113
+ #
114
+ # GITUP_SKIP_MIGRATIONS:
115
+ # Whether or not to skip the db:migration steps after the git update
116
+ #
117
+ # Options:
118
+ # 0: (default) do not skip the db:migration steps
119
+ # 1: skip the db:migration steps
120
+ #
121
+ # GITUP_RUN_MIGRATIONS_FN:
122
+ # Function to run migrations. Occurs after git update and post-update steps
123
+ #
124
+ # Default:
125
+ # GITUP_RUN_MIGRATIONS_FN=__gitup_migrations
126
+ #
127
+ GITUP_SKIP_MIGRATIONS=1
128
+ # GITUP_RUN_MIGRATIONS_FN=__gitup_migrations
data/Gemfile.lock CHANGED
@@ -2,7 +2,7 @@ PATH
2
2
  remote: .
3
3
  specs:
4
4
  rspec-graphql_response (0.1.0)
5
- rspec
5
+ rspec (~> 3.10)
6
6
 
7
7
  GEM
8
8
  remote: https://rubygems.org/
@@ -39,9 +39,9 @@ PLATFORMS
39
39
  DEPENDENCIES
40
40
  bundler (~> 1.17)
41
41
  graphql (~> 1.12)
42
- pry
43
- pry-byebug
44
- rake
42
+ pry (~> 0.14)
43
+ pry-byebug (~> 3.8)
44
+ rake (>= 12.0)
45
45
  rspec-graphql_response!
46
46
 
47
47
  BUNDLED WITH
data/README.md CHANGED
@@ -3,6 +3,15 @@
3
3
  RSpec::GraphQLResponse provides a series of RSpec matchers, helper methods, and other configuration to help simplify
4
4
  the testing of responses from the `graphql-ruby` gem and the `<GraphQLSchemaName>.execute` method.
5
5
 
6
+ ## "Why Should I Use This?"
7
+
8
+ There are a number of built-in helper methods and matchers that will allow you to skip the copy & paste work of executing
9
+ a GraphQL Schema `.execute`. Additionally, there are custom matchers and other bits that will help simplify your work in
10
+ validating common peices of a graphql response.
11
+
12
+ Lastly, the work in this gem is geared toward customization for your own application's needs. Every API call used for building
13
+ the pieces of this gem are available to you, directly, in the `API / Development` documentation, below.
14
+
6
15
  ## Installation
7
16
 
8
17
  Your app must have `graphql-ruby` and `rspec`. With that done, add this line to your application's Gemfile:
@@ -21,71 +30,55 @@ Or install it yourself as:
21
30
 
22
31
  ## Full Documentation
23
32
 
24
- The full documentation for RSpec::GraphQLResponse can be found in the `/docs`
25
- folder.
26
-
27
- Custom Matchers:
28
- * [have_errors matcher](/docs/have_errors.md) - validates errors, or lack of, on
29
- the GraphQL response
30
-
31
- Helper Methods:
32
- * [operation](/docs/operation.md) - retrieves the results of a named operation
33
- from the GraphQL response
33
+ * [Release Notes](/RELEASE_NOTES.md)
34
+ * [Upgrade Guide](/UPGRADE.md)
34
35
 
35
- Internal API / Development
36
+ The full documentation for RSpec::GraphQLResponse can be found in the `/docs` folder.
36
37
 
37
- * [.add_matcher](/docs/add_matcher.md) - add a custom RSpec matcher to the
38
- GraphQLResponse matchers
39
- * [.add_validator](/docs/add_validator.md) - add a custom validator to be used
40
- by the custom matchers
38
+ Configuration:
39
+ * [RSpec::GraphQLResponse.configure](/docs/configuration.md)
40
+ * [Spec Type :graphql](/docs/graphql_spec_type.md)
41
41
 
42
- ## Configuration
42
+ Custom Matchers:
43
+ * [have_errors](/docs/have_errors.md) - validates errors, or lack of, on the GraphQL response
44
+ * [have_operation](/docs/have_operation.md) - validates the presence of a specified graphql operation in the graphql response
43
45
 
44
- To get things rolling, add a configuration block to your `spec_helper.rb` (or other file that gets included in specs, as
45
- desired). Within this block, you'll need to provide the GraphQL Schema to use for query execution.
46
+ Context / Describe Helper Methods:
47
+ * [execute_graphql](/docs/execute_graphql.md) - executes a graphql call with the registered schema, query, variables and context
48
+ * [graphql_query](/docs/execute_graphql.md) - the query to execute
49
+ * [graphql_variables](/docs/execute_graphql.md) - a hash of variables the query expects
50
+ * [graphql_context](/docs/execute_graphql.md) - the `context` of a query or mutation's resolver
46
51
 
47
- ```ruby
48
- RSpec::GraphQLResponse.configure |config| do
52
+ Spec Helper Methods:
53
+ * [response](/docs/response.md) - the response, as JSON, of the executed graphql query
54
+ * [operation](/docs/operation.md) - retrieves the results of a named operation from the GraphQL response
49
55
 
50
- config.graphql_schema = MyGraphQLSchema
51
-
52
- end
53
- ```
56
+ API / Development
57
+ * [.add_matcher](/docs/add_matcher.md) - add a custom RSpec matcher to the GraphQLResponse matchers
58
+ * [.add_validator](/docs/add_validator.md) - add a custom validator to be used by the custom matchers
59
+ * [.add_helper](/docs/add_helper.md) - add helper methods to your specs, made avialable in `it` or `describe` / `context` blocks
54
60
 
55
61
  ## Getting Started
56
62
 
57
- There are a number of built-in helper methods and matchers that will allow you to skip the copy & paste work of executing
58
- a GraphQL Schema `.execute`. These include:
59
-
60
- Spec types:
61
-
62
- * `type: :graphql`
63
-
64
- Helper methods:
65
-
66
- * `execute_graphql`
67
- * `response`
68
- * `operation`
63
+ There are only a couple of bits you need to get started:
69
64
 
70
- Matchers:
71
-
72
- * `have_errors`
73
-
74
- ### The `:graphql` Spec Type
75
-
76
- To use these custom helpers and matchers, you need to specificy `type: :graphql` as part of your spec's description. For exmaple,
65
+ * configuration of a GraphQL Schema in [`RSpec::GraphQLResponse.configure`](/docs/configuration.md)
66
+ * the inclusion of [`type: :graphql`](/docs/graphql_spec_type.md) in your `RSpec.describe` call
77
67
 
78
68
  ```ruby
79
- RSpec.describe Some::Thing, type: :graphql do
80
-
81
- # ...
69
+ RSpec::GraphQLResponse.configure do |config|
70
+ config.graphql_schema = MyGraphQLSchema
71
+ end
82
72
 
73
+ RSpec.describe My::Cool::Thing, type: :graphql do
74
+ # ...
83
75
  end
84
76
  ```
85
77
 
86
- With this `type` set, your specs will have access to all of the `RSpec::GraphQLResponse` code.
78
+ Beyond these two basic needs, understanding the reason for this gem's existence can be useful in figuring out what the gem
79
+ does, and what methods and options are available.
87
80
 
88
- ### Helper Methods
81
+ ## How We Got Here
89
82
 
90
83
  Executing a GraphQL call from RSpec is not the most challening code to write:
91
84
 
@@ -121,16 +114,14 @@ in your specs.
121
114
 
122
115
  ```ruby
123
116
  RSpec.describe Some::Thing, type: :graphql do
124
- let(:query) do
125
- <<-GQL
126
- query ListCharacters{
127
- characters {
128
- id
129
- name
130
- }
117
+ graphql_query <<-GQL
118
+ query ListCharacters{
119
+ characters {
120
+ id
121
+ name
131
122
  }
132
- GQL
133
- end
123
+ }
124
+ GQL
134
125
 
135
126
  it "executes the query" do
136
127
  response = execute_graphql.to_h
@@ -147,16 +138,14 @@ way. The reduce this, `RSpec::GraphQLResponse` provides a built-in `response` he
147
138
 
148
139
  ```ruby
149
140
  RSpec.describe Some::Thing, type: :graphql do
150
- let(:query) do
151
- <<-GQL
152
- query {
153
- characters {
154
- id
155
- name
156
- }
141
+ graphql_query <<-GQL
142
+ query ListCharacters{
143
+ characters {
144
+ id
145
+ name
157
146
  }
158
- GQL
159
- end
147
+ }
148
+ GQL
160
149
 
161
150
  it "executes the query" do
162
151
  expect(response).to include(
@@ -176,16 +165,14 @@ nested hash checking, use the built-in `operation` method to retrieve the `chara
176
165
 
177
166
  ```ruby
178
167
  RSpec.describe Some::Thing, type: :graphql do
179
- let(:query) do
180
- <<-GQL
181
- query {
182
- characters {
183
- id
184
- name
185
- }
168
+ graphql_query <<-GQL
169
+ query ListCharacters{
170
+ characters {
171
+ id
172
+ name
186
173
  }
187
- GQL
188
- end
174
+ }
175
+ GQL
189
176
 
190
177
  it "executes the query" do
191
178
  characters = operation(:characters)
data/RELEASE_NOTES.md ADDED
@@ -0,0 +1,39 @@
1
+ # Release Notes
2
+
3
+ Release notes for various versions of RSpec::GraphQLResponse
4
+
5
+ See [the upgrade guide](/UPGRADE.md) for details on changes between versions and how to upgrade.
6
+
7
+ ## v0.2.0 - GraphQL Configuration DSL and Refactorings
8
+
9
+ Misc changes and corrections, some new features, and generally trying to create a more robust
10
+ and usable experience, right out of the box.
11
+
12
+ ### New Features
13
+
14
+ * Significantly improved documentation
15
+ * `have_operation` matcher
16
+ * GraphQL configuration DSL
17
+ * `graphql_query`
18
+ * `graphql_variables`
19
+ * `graphql_context`
20
+ * Describe/Context level RSpec helper methods via `.add_context_helper` DSL
21
+
22
+ ### Breaking Changes
23
+
24
+ This release changes the graphql query, variables and context configuration away from `let` vars
25
+ and into a proper DSL.
26
+
27
+ ### Bug Fixes
28
+
29
+ Lots of misc bug fixes, including caching of values, ensuring things work in nested contexts, etc.
30
+
31
+ ## v0.1.0 - Initial Release
32
+
33
+ Early beta work to get this out the door and begin adoption
34
+
35
+ * `have_errors` matcher
36
+ * `operation` helper
37
+ * `response` helper
38
+ * `execute_graphql` helper
39
+ * DSL for adding custom matchers, validators, and helpers
data/UPGRADE.md ADDED
@@ -0,0 +1,37 @@
1
+ # Upgrade Guide
2
+
3
+ ## v0.1.0 to v0.2.0
4
+
5
+ There is a breaking change between v0.1.0 and v0.2.0 regarding the configuration of graphql queries, variables and context.
6
+ Previously, you defined these three items with `let` in rspec:
7
+
8
+ ```ruby
9
+ let(:graphql_query) { ... }
10
+ ```
11
+
12
+ In v0.2.0, this changes to a new DSL for each of these three items:
13
+
14
+ ```ruby
15
+ RSpec.describe My::Stuff, type: :graphql do
16
+
17
+ graphql_query <<-GQL
18
+ query Characters($name: String) {
19
+ characters(name: $name) {
20
+ id
21
+ name
22
+ }
23
+ }
24
+ GQL
25
+
26
+ graphql_variables({
27
+ name: "Jam"
28
+ })
29
+
30
+ graphql_context({
31
+ some: "context here",
32
+ current_user: some_user
33
+ })
34
+
35
+ it " ... "
36
+ end
37
+ ```
@@ -0,0 +1,95 @@
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_query` and other DSL methods for configuring graphql calls are a great example of
62
+ context level helpers.
63
+
64
+ To create a context helper, call `RSpec::GraphQLResponse.add_context_helper(name, &helper_block)`. The params
65
+ are the same as `.add_helper`, but the resulting method will be made available in `describe` and `context`
66
+ blocks.
67
+
68
+ ```ruby
69
+ RSpec::GraphQLResponse.add_context_helper :my_setting do |some_arg|
70
+ @my_setting = some_arg
71
+ end
72
+ ```
73
+
74
+ In this simple example, a method called `my_setting` is created, and it stores a value in the instance variable
75
+ `@my_setting`. This takes advantage of RSpec's native ability to handle instance variables in describe and
76
+ context blocks, allowing the variable to exist withing the hierarchy of objects for a given spec.
77
+
78
+ With that defined, it can be used within a spec:
79
+
80
+ ```ruby
81
+ RSpec.describe Cool::Thing, type: :graphql do
82
+
83
+ my_setting "this is a cool setting!"
84
+
85
+
86
+ it "has the setting" do
87
+ setting = self.class.instance_variable_get(:@my_setting)
88
+
89
+ expect(setting).to eq("this is a cool setting!")
90
+ end
91
+ end
92
+ ```
93
+
94
+ You may want to provide a better way of retrieving the `@my_setting` var from the class, but this at least
95
+ 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,43 @@
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
+ graphql_query <<-GQL
9
+ query SomeThing($name: String) {
10
+ characters(name: $name) {
11
+ id
12
+ name
13
+ }
14
+ }
15
+ GQL
16
+
17
+ graphql_variables {
18
+ name: "Jam"
19
+ }
20
+
21
+ grapql_context {
22
+ current_user: "some user or whatever you need"
23
+ }
24
+
25
+ it "executes and does the thing with the vars and context" do
26
+ # ... expect things here
27
+ end
28
+ end
29
+ ```
30
+
31
+ ## Available Configuration Methods
32
+
33
+ ### `graphql_query`
34
+
35
+ A string - most commonly a ruby heredoc - for the graphql query to execute
36
+
37
+ ### `graphql_variables`
38
+
39
+ A hash of keys and values that the query expects to use
40
+
41
+ ### `graphql_context`
42
+
43
+ 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_query <<-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_query <<-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/response.md ADDED
@@ -0,0 +1 @@
1
+ # Check the GraphQL Response with Helper `response`
@@ -1,4 +1,4 @@
1
- require "RSpec"
1
+ require "rspec"
2
2
 
3
3
  require_relative "graphql_response/version"
4
4
  require_relative "graphql_response/configuration"
@@ -1,9 +1,20 @@
1
1
  module RSpec
2
2
  module GraphQLResponse
3
- def self.add_helper(name, &helper)
3
+ def self.add_context_helper(name, &helper)
4
+ self.add_helper(name, scope: :context, &helper)
5
+ end
6
+
7
+ def self.add_helper(name, scope: :spec, &helper)
4
8
  helper_module = Module.new do |mod|
5
9
  mod.define_method(name) do |*args|
6
- @result ||= self.instance_exec(*args, &helper)
10
+ instance_var = "@#{name}".to_sym
11
+
12
+ if self.instance_variables.include? instance_var
13
+ return self.instance_variable_get(instance_var)
14
+ end
15
+
16
+ result = self.instance_exec(*args, &helper)
17
+ self.instance_variable_set(instance_var, result)
7
18
  end
8
19
  end
9
20
 
@@ -11,13 +22,27 @@ module RSpec
11
22
  config.after(:each) do
12
23
  helper_module.instance_variable_set(:@result, nil)
13
24
  end
14
- end
15
25
 
16
- self.include(helper_module)
26
+ module_method = if scope == :spec
27
+ :include
28
+ elsif scope == :context
29
+ :extend
30
+ else
31
+ raise ArgumentError, "A helper method's scope must be either :spec or :describe"
32
+ end
33
+
34
+ config.send(module_method, helper_module, type: :graphql)
35
+ end
17
36
  end
18
37
  end
19
38
  end
20
39
 
40
+ # describe level helpers
41
+ require_relative "helpers/graphql_query"
42
+ require_relative "helpers/graphql_variables"
43
+ require_relative "helpers/graphql_context"
44
+
45
+ # spec level helpers
21
46
  require_relative "helpers/operation"
22
47
  require_relative "helpers/response"
23
48
  require_relative "helpers/execute_graphql"
@@ -1,4 +1,12 @@
1
1
  RSpec::GraphQLResponse.add_helper :execute_graphql do
2
2
  config = RSpec::GraphQLResponse.configuration
3
- config.graphql_schema.execute(query)
3
+
4
+ query = get_graphql_query if respond_to? :get_graphql_query
5
+ query_vars = get_graphql_variables if respond_to? :get_graphql_variables
6
+ query_context = get_graphql_context if respond_to? :get_graphql_context
7
+
8
+ config.graphql_schema.execute(query, {
9
+ variables: query_vars,
10
+ context: query_context
11
+ })
4
12
  end
@@ -0,0 +1,3 @@
1
+ RSpec::GraphQLResponse.add_context_helper :graphql_context do |ctx|
2
+ self.define_method(:get_graphql_context) { ctx }
3
+ end
@@ -0,0 +1,3 @@
1
+ RSpec::GraphQLResponse.add_context_helper :graphql_query do |gql|
2
+ self.define_method(:get_graphql_query) { gql }
3
+ end
@@ -0,0 +1,3 @@
1
+ RSpec::GraphQLResponse.add_context_helper :graphql_variables do |vars|
2
+ self.define_method(:get_graphql_variables) { vars }
3
+ end
@@ -12,3 +12,4 @@ module RSpec
12
12
  end
13
13
 
14
14
  require_relative "matchers/have_errors"
15
+ require_relative "matchers/have_operation"
@@ -11,7 +11,7 @@ RSpec::GraphQLResponse.add_matcher :have_errors do |count = nil|
11
11
  @result.valid?
12
12
  end
13
13
 
14
- failure_message do |response|
14
+ failure_message do |_|
15
15
  @result.reason
16
16
  end
17
17
 
@@ -27,7 +27,7 @@ RSpec::GraphQLResponse.add_matcher :have_errors do |count = nil|
27
27
  @result.valid?
28
28
  end
29
29
 
30
- failure_message_when_negated do |response|
30
+ failure_message_when_negated do |_|
31
31
  @result.reason
32
32
  end
33
33
 
@@ -0,0 +1,23 @@
1
+ RSpec::GraphQLResponse.add_matcher :have_operation do |operation_name|
2
+ match do |response|
3
+ validator = RSpec::GraphQLResponse.validator(:have_operation)
4
+
5
+ @result = validator.validate(response, operation_name: operation_name)
6
+ @result.valid?
7
+ end
8
+
9
+ failure_message do |_|
10
+ @result.reason
11
+ end
12
+
13
+ match_when_negated do |response|
14
+ validator = RSpec::GraphQLResponse.validator(:have_operation)
15
+
16
+ @result = validator.validate_negated(response, operation_name: operation_name)
17
+ @result.valid?
18
+ end
19
+
20
+ failure_message_when_negated do |_|
21
+ @result.reason
22
+ end
23
+ end
@@ -11,6 +11,11 @@ module RSpec
11
11
  @validators[name] = validator_class
12
12
  end
13
13
 
14
+ def self.remove_validator(name)
15
+ @validators ||= {}
16
+ @validators.delete(:key)
17
+ end
18
+
14
19
  def self.validator(name)
15
20
  @validators[name].new
16
21
  end
@@ -18,3 +23,4 @@ module RSpec
18
23
  end
19
24
 
20
25
  require_relative "validators/have_errors"
26
+ require_relative "validators/have_operation"
@@ -0,0 +1,24 @@
1
+ RSpec::GraphQLResponse.add_validator :have_operation do
2
+ failure_message :nil, "Cannot evaluate operations on nil"
3
+ failure_message :not_found, ->(expected, actual) { "Expected to find operation result named #{expected}, but did not find it\n\t#{actual}" }
4
+
5
+ validate do |response, operation_name:|
6
+ next fail_validation(:nil) unless response.is_a? Hash
7
+
8
+ op = response.dig("data", operation_name.to_s)
9
+ next fail_validation(:not_found, operation_name, response) if op.nil?
10
+
11
+ pass_validation
12
+ end
13
+
14
+ failure_message :found, ->(expected, actual) { "Expected not to find operation result named #{expected}, but found it\n\t#{actual}" }
15
+
16
+ validate_negated do |response, operation_name:|
17
+ next fail_validation(:nil) unless response.is_a? Hash
18
+
19
+ op = response.dig("data", operation_name.to_s)
20
+ next fail_validation(:found, operation_name, response) unless op.nil?
21
+
22
+ pass_validation
23
+ end
24
+ end
@@ -1,5 +1,5 @@
1
1
  module RSpec
2
2
  module GraphQLResponse
3
- VERSION = "0.1.0"
3
+ VERSION = "0.2.0"
4
4
  end
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rspec-graphql_response
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - River Lynn Bailey
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-02-24 00:00:00.000000000 Z
11
+ date: 2021-03-02 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -102,34 +102,48 @@ executables: []
102
102
  extensions: []
103
103
  extra_rdoc_files: []
104
104
  files:
105
+ - ".github/workflows/test.yml"
105
106
  - ".gitignore"
107
+ - ".gituprc"
106
108
  - ".pryrc"
107
109
  - ".rspec"
108
110
  - ".ruby-version"
109
- - ".travis.yml"
110
111
  - CODE_OF_CONDUCT.md
111
112
  - Gemfile
112
113
  - Gemfile.lock
113
114
  - LICENSE.txt
114
115
  - README.md
116
+ - RELEASE_NOTES.md
115
117
  - Rakefile
118
+ - UPGRADE.md
116
119
  - bin/console
117
120
  - bin/rspec
118
121
  - bin/setup
122
+ - docs/add_helper.md
119
123
  - docs/add_matcher.md
120
124
  - docs/add_validator.md
125
+ - docs/configuration.md
126
+ - docs/execute_graphql.md
127
+ - docs/graphql_spec_type.md
121
128
  - docs/have_errors.md
129
+ - docs/have_operation.md
122
130
  - docs/operation.md
131
+ - docs/response.md
123
132
  - lib/rspec/graphql_response.rb
124
133
  - lib/rspec/graphql_response/configuration.rb
125
134
  - lib/rspec/graphql_response/helpers.rb
126
135
  - lib/rspec/graphql_response/helpers/execute_graphql.rb
136
+ - lib/rspec/graphql_response/helpers/graphql_context.rb
137
+ - lib/rspec/graphql_response/helpers/graphql_query.rb
138
+ - lib/rspec/graphql_response/helpers/graphql_variables.rb
127
139
  - lib/rspec/graphql_response/helpers/operation.rb
128
140
  - lib/rspec/graphql_response/helpers/response.rb
129
141
  - lib/rspec/graphql_response/matchers.rb
130
142
  - lib/rspec/graphql_response/matchers/have_errors.rb
143
+ - lib/rspec/graphql_response/matchers/have_operation.rb
131
144
  - lib/rspec/graphql_response/validators.rb
132
145
  - lib/rspec/graphql_response/validators/have_errors.rb
146
+ - lib/rspec/graphql_response/validators/have_operation.rb
133
147
  - lib/rspec/graphql_response/validators/validation_base.rb
134
148
  - lib/rspec/graphql_response/validators/validation_result.rb
135
149
  - lib/rspec/graphql_response/validators/validation_runner.rb
data/.travis.yml DELETED
@@ -1,7 +0,0 @@
1
- ---
2
- sudo: false
3
- language: ruby
4
- cache: bundler
5
- rvm:
6
- - 2.6.6
7
- before_install: gem install bundler -v 1.17.2