rspec-graphql-integration 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: 39c4ff53e15be806adbd54539c31cd15f92b7733f6161fbbd3d90b4d827d6d23
4
- data.tar.gz: '0780649082510a767cc3bf76225539837a1f0bb4caef92b5f610406b7567d5de'
3
+ metadata.gz: c741c1c9dcf41ac20bad890effdfde5ffa121b63b60f6704f07c0638a6001e6e
4
+ data.tar.gz: 74742a1424ec5ff2587bf945eab4cc5bf8ea059552da4f9b61ef01676b284477
5
5
  SHA512:
6
- metadata.gz: 26c78cddba206449f656a17f8ec4e0fe4edf6527b1104be5fd490a8e785be56bec083b2f8d21c155c696b201989a0036898c05f59b3576de3f2e0ba1189c2e66
7
- data.tar.gz: a6ae1678e9c113928c98c3c45cd3e7ef33aa432cd9a03eb2ceedc80aa350fd8d1e44d2f77417eae5d5925b36ebdc69b386dedae9b89b595b311274ca5ca7e78f
6
+ metadata.gz: 1dc6131393b96403f4054cb9a75c9d44c00a23dc6e5b101525290f1ba9f55d05b8ac4e3d071df262c14f53c4508d7a6f72357a10ca30eb611e1ad760fa3aa7bd
7
+ data.tar.gz: c6ed7377e513b4e12589f0ae8f1986bbedfbe8c43c31e773427e7b96926089ecac242b930cab4c858994b001776625e34af078b2fb56e2fed42931884b8acebb
@@ -4,6 +4,7 @@ module RSpec
4
4
  module GraphqlIntegration
5
5
  def self.initialize_configuration(config) # rubocop:disable Metrics/MethodLength
6
6
  config.add_setting(:graphql_schema_class, default: nil)
7
+ config.add_setting(:graphql_put_files_in_folder, default: false)
7
8
 
8
9
  config.include(RSpec::GraphqlIntegration::Matchers::DeepEq, type: :graphql)
9
10
  config.include(RSpec::GraphqlIntegration::Matchers::MatchGraphqlResponse, type: :graphql)
@@ -2,12 +2,10 @@ module RSpec
2
2
  module GraphqlIntegration
3
3
  module Matchers
4
4
  ##
5
- # This matcher recursively compares nested Ruby Hashes and Arrays while ignoring
5
+ # This helper method recursively compares nested Ruby Hashes and Arrays while ignoring
6
6
  # the order of elements in Arrays.
7
7
  module DeepEq
8
- extend RSpec::Matchers::DSL
9
-
10
- matcher(:deep_eq) { |expected| match { |actual| deep_eq?(actual, expected) } }
8
+ module_function
11
9
 
12
10
  def deep_eq?(actual, expected)
13
11
  return arrays_deep_eq?(actual, expected) if expected.is_a?(Array) && actual.is_a?(Array)
@@ -20,8 +18,8 @@ module RSpec
20
18
  def arrays_deep_eq?(actual, expected)
21
19
  expected = expected.clone
22
20
 
23
- actual.each do |array|
24
- index = expected.find_index { |element| deep_eq?(array, element) }
21
+ actual.each do |actual_array|
22
+ index = expected.find_index { |expected_array| deep_eq?(actual_array, expected_array) }
25
23
  return false if index.nil?
26
24
 
27
25
  expected.delete_at(index)
@@ -1,3 +1,5 @@
1
+ require "json"
2
+
1
3
  module RSpec
2
4
  module GraphqlIntegration
3
5
  module Matchers
@@ -7,104 +9,239 @@ module RSpec
7
9
  extend RSpec::Matchers::DSL
8
10
 
9
11
  ##
10
- # This error is thrown when a variable is missing that is required for
11
- # the GraphQL response matcher.
12
- class TestVariableMissing < ArgumentError
13
- def initialize(variable_name, example_value)
12
+ # This error is raised when the schema class is not set in the RSpec configuration.
13
+ class SchemaNotSetError < StandardError
14
+ def initialize
14
15
  super <<~TEXT
15
- Test variable #{variable_name} is missing.
16
+ Please define
17
+
18
+ config.graphql_schema_class
16
19
 
17
- Please define it, e.g. with:
18
- let(:#{variable_name}) { #{example_value} }
20
+ in your spec_helper.rb
19
21
  TEXT
20
22
  end
21
23
  end
22
24
 
23
25
  ##
24
- # Defines the required variables as a hash for a test that uses
25
- # the GraphQL response matcher.
26
- #
27
- # Key is the variable name, value is an example value.
28
- REQUIRED_TEST_VARIABLES = { test_dir: "__FILE__" }.freeze
26
+ # This error is thrown if neither the default request file is present nor
27
+ # the request_file_overwrite variable is set in a test.
28
+ class DefaultRequestFileMissing < StandardError
29
+ def initialize(default_request_file)
30
+ super <<~TEXT
31
+ No default request file found for this test.
32
+
33
+ Please create either a default request file:
34
+ #{default_request_file}
35
+
36
+ or define a file with the request_file_overwrite variable:
37
+ let(:request_file_overwrite) { File.join(File.dirname(__FILE__), "my_request_file.graphql") }
38
+ TEXT
39
+ end
40
+ end
41
+
42
+ ##
43
+ # This error is thrown if neither the default request file is present nor
44
+ # the response_file_overwrite variable is set in a test.
45
+ class DefaultResponseFileMissing < StandardError
46
+ def initialize(default_response_file)
47
+ super <<~TEXT
48
+ No default response file found for this test.
49
+
50
+ Please create either a default response file:
51
+ #{default_response_file}
52
+
53
+ or define a file with the response_file_overwrite variable:
54
+ let(:response_file_overwrite) { File.join(File.dirname(__FILE__), "my_response_file.graphql") }
55
+ TEXT
56
+ end
57
+ end
29
58
 
30
59
  matcher(:match_graphql_response) do |_expected| # rubocop:disable Metrics/BlockLength
31
60
  match do |_actual|
32
- check_variables!
61
+ # expected_response needs to be loaded first because it might contain variables
62
+ # that are required in the request and that aren't loaded yet. Such variables
63
+ # could be database objects that could be created while loading the response.
64
+ expected_response
33
65
 
34
- # We need to test the responses with be_deep_equal so that we ignore
66
+ # We need to test the responses with DeepEq so that we ignore
35
67
  # the order in nested hashes.
36
- expect(actual_response).to deep_eq(expected_response)
37
- rescue RSpec::Expectations::ExpectationNotMetError => e
38
- @error = e
39
- raise
68
+ DeepEq.deep_eq?(actual_response, expected_response)
40
69
  end
41
70
 
42
71
  # For the failure message, we want to show the diff between the actual and
43
72
  # the expected response and the standard eq matcher from RSpec does that best.
44
73
  failure_message { expect(actual_response).to eq(expected_response) }
45
74
 
46
- def check_variables!
47
- REQUIRED_TEST_VARIABLES.each do |variable_name, example_value|
48
- unless defined?(send(variable_name))
49
- raise TestVariableMissing.new(variable_name, example_value)
75
+ ##
76
+ # Loads the response file and substitutes the variables in the response file.
77
+ def expected_response
78
+ @expected_response ||=
79
+ begin
80
+ expected_response =
81
+ load_response(
82
+ response_file,
83
+ defined?(response_variables) ? response_variables : {},
84
+ )
85
+
86
+ expected_response = [expected_response] unless expected_response.is_a?(Array)
87
+
88
+ expected_response
50
89
  end
90
+ end
91
+
92
+ ##
93
+ # Executes the query with the context and the variables against the schema and
94
+ # returns the response.
95
+ def actual_response
96
+ @actual_response ||=
97
+ begin
98
+ response =
99
+ schema_class.execute(
100
+ File.read(request_file),
101
+ context: defined?(context) ? context : {},
102
+ variables: defined?(request_variables) ? request_variables : {},
103
+ )
104
+
105
+ response.values
106
+ end
107
+ end
108
+
109
+ ##
110
+ # This method tries to get the file path for the file that is running the tests.
111
+ #
112
+ # The magic of this method can be overwritten by defining:
113
+ # let(:test_file_overwrite) { __FILE__ }
114
+ def test_file
115
+ return test_file_overwrite if defined?(test_file_overwrite)
116
+
117
+ caller_location =
118
+ caller_locations.find { |location| location.path.include?("/spec/graphql") }
119
+
120
+ caller_location.path
121
+ end
122
+
123
+ ##
124
+ # This method defines the folder where the request and response files are stored.
125
+ #
126
+ # It's either the folder of the file that is running the tests or a subfolder
127
+ # named like the test file.
128
+ def files_folder
129
+ if RSpec.configuration.graphql_put_files_in_folder
130
+ test_file.gsub("_spec.rb", "")
131
+ else
132
+ File.dirname(test_file)
51
133
  end
52
134
  end
53
135
 
54
- def expected_response
55
- expected_response = load_response(test_dir, response_template, response_variables)
136
+ ##
137
+ # This method tries to get the file path for the request file.
138
+ #
139
+ # If the request_file_overwrite variable is set, it uses that.
140
+ #
141
+ # raises DefaultRequestFileMissing if no request file is found.
142
+ def request_file
143
+ if defined?(request_file_overwrite)
144
+ return File.join(files_folder, request_file_overwrite)
145
+ end
56
146
 
57
- expected_response = [expected_response] unless expected_response.is_a?(Array)
147
+ default_request_file = File.join(files_folder, default_request_file_name)
58
148
 
59
- expected_response
149
+ unless File.exist?(default_request_file)
150
+ raise DefaultRequestFileMissing, default_request_file
151
+ end
152
+
153
+ default_request_file
60
154
  end
61
155
 
62
- def actual_response
63
- response =
64
- schema_class.execute(
65
- load_query(test_dir, query_file),
66
- context: context,
67
- variables: defined?(request_variables) ? request_variables : {},
68
- )
69
-
70
- response.values
156
+ ##
157
+ # The default request file is either called like the test file without _spec
158
+ # but with .graphql or it's in the subfolder of the test file called "request.graphql".
159
+ def default_request_file_name
160
+ if RSpec.configuration.graphql_put_files_in_folder
161
+ "request.graphql"
162
+ else
163
+ test_file.split("/").last.gsub("_spec.rb", ".graphql")
164
+ end
71
165
  end
72
166
 
167
+ ##
168
+ # This method tries to get the file path for the response file.
169
+ #
170
+ # If the response_file_overwrite variable is set, it uses that.
171
+ #
172
+ # raises DefaultRequestFileMissing if no response file is found.
173
+ def response_file
174
+ if defined?(response_file_overwrite)
175
+ return File.join(files_folder, response_file_overwrite)
176
+ end
177
+
178
+ default_response_file = File.join(files_folder, default_response_file_name)
179
+
180
+ unless File.exist?(default_response_file)
181
+ raise DefaultResponseFileMissing, default_response_file
182
+ end
183
+
184
+ default_response_file
185
+ end
186
+
187
+ ##
188
+ # The default response file is either called like the test file without _spec
189
+ # but with .json or it's in the subfolder of the test file called "response.json".
190
+ def default_response_file_name
191
+ if RSpec.configuration.graphql_put_files_in_folder
192
+ "response.json"
193
+ else
194
+ test_file.split("/").last.gsub("_spec.rb", ".json")
195
+ end
196
+ end
197
+
198
+ ##
199
+ # This method gets the schema class from the RSpec configuration.
200
+ #
201
+ # If schema_class_overwrite is set, it uses that.
202
+ #
203
+ # raises SchemaNotSetError if the schema class is not set.
73
204
  def schema_class
74
205
  # It's possible to overwrite the schema class if an app has multiple schemas.
75
206
  return schema_class_overwrite if defined?(schema_class_overwrite)
76
207
 
77
- if RSpec.configuration.graphql_schema_class.nil? && !Object.const_defined?(:Schema)
78
- raise "Please define config.graphql_schema_class in your rails_helper.rb"
79
- end
208
+ raise SchemaNotSetError if RSpec.configuration.graphql_schema_class.nil?
80
209
 
81
210
  RSpec.configuration.graphql_schema_class
82
211
  end
83
212
  end
84
213
 
85
- ##
86
- # Loads a query in a GraphQL file.
87
- #
88
- # Example:
89
- # load_query(__FILE__, 'current_user/query.graphql'),
90
- def load_query(dir, filename)
91
- File.read(File.join(File.dirname(dir), filename))
92
- end
93
-
94
214
  ##
95
215
  # Loads a response in a JSON file and substitute the passed variables that
96
216
  # are surrounded by {{...}} in the file.
97
217
  #
98
218
  # Example:
99
219
  # load_response(
100
- # __FILE__,
101
- # 'current_user/response.json',
220
+ # "current_user.json",
102
221
  # {
103
- # user_id: user.id,
222
+ # user_id: 1,
104
223
  # },
105
224
  # )
106
- def load_response(dir, filename, variables = {})
107
- json_file = File.read(File.join(File.dirname(dir), filename))
225
+ #
226
+ # current_user.json:
227
+ # {
228
+ # "data": {
229
+ # "currentUser": {
230
+ # "id": "{{user_id}}",
231
+ # }
232
+ # }
233
+ # }
234
+ #
235
+ # Result:
236
+ # {
237
+ # "data": {
238
+ # "currentUser": {
239
+ # "id": "1",
240
+ # }
241
+ # }
242
+ # }
243
+ def load_response(filename, variables = {})
244
+ json_file = File.read(filename)
108
245
  variables.each { |key, value| json_file.gsub!("\"{{#{key}}}\"", JSON.dump(value)) }
109
246
 
110
247
  JSON.parse(json_file)
@@ -1,5 +1,5 @@
1
1
  module RSpec
2
2
  module GraphqlIntegration
3
- VERSION = "0.1.0".freeze
3
+ VERSION = "0.2.0".freeze
4
4
  end
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rspec-graphql-integration
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
  - Peter Gundel
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2022-12-31 00:00:00.000000000 Z
11
+ date: 2023-01-04 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: graphql
@@ -38,6 +38,20 @@ dependencies:
38
38
  - - ">="
39
39
  - !ruby/object:Gem::Version
40
40
  version: 3.0.0
41
+ - !ruby/object:Gem::Dependency
42
+ name: byebug
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
41
55
  - !ruby/object:Gem::Dependency
42
56
  name: prettier
43
57
  requirement: !ruby/object:Gem::Requirement
@@ -52,6 +66,48 @@ dependencies:
52
66
  - - ">="
53
67
  - !ruby/object:Gem::Version
54
68
  version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: pry
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: pry-byebug
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ - !ruby/object:Gem::Dependency
98
+ name: rspec
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ">="
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - ">="
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
55
111
  - !ruby/object:Gem::Dependency
56
112
  name: rubocop
57
113
  requirement: !ruby/object:Gem::Requirement
@@ -66,6 +122,34 @@ dependencies:
66
122
  - - ">="
67
123
  - !ruby/object:Gem::Version
68
124
  version: '0'
125
+ - !ruby/object:Gem::Dependency
126
+ name: rubocop-rspec
127
+ requirement: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - ">="
130
+ - !ruby/object:Gem::Version
131
+ version: '0'
132
+ type: :development
133
+ prerelease: false
134
+ version_requirements: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - ">="
137
+ - !ruby/object:Gem::Version
138
+ version: '0'
139
+ - !ruby/object:Gem::Dependency
140
+ name: simplecov
141
+ requirement: !ruby/object:Gem::Requirement
142
+ requirements:
143
+ - - ">="
144
+ - !ruby/object:Gem::Version
145
+ version: '0'
146
+ type: :development
147
+ prerelease: false
148
+ version_requirements: !ruby/object:Gem::Requirement
149
+ requirements:
150
+ - - ">="
151
+ - !ruby/object:Gem::Version
152
+ version: '0'
69
153
  description:
70
154
  email:
71
155
  - gundel.peter@gmail.com
@@ -91,14 +175,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
91
175
  requirements:
92
176
  - - ">="
93
177
  - !ruby/object:Gem::Version
94
- version: '2.6'
178
+ version: '2.7'
95
179
  required_rubygems_version: !ruby/object:Gem::Requirement
96
180
  requirements:
97
181
  - - ">="
98
182
  - !ruby/object:Gem::Version
99
183
  version: '0'
100
184
  requirements: []
101
- rubygems_version: 3.3.7
185
+ rubygems_version: 3.1.6
102
186
  signing_key:
103
187
  specification_version: 4
104
188
  summary: An RSpec plugin to simplify integration tests for GraphQL