rspec-graphql_response 0.1.0 → 0.2.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 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