rspec-graphql-integration 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/lib/rspec/graphql_integration/configuration.rb +30 -0
- data/lib/rspec/graphql_integration/matchers/deep_eq.rb +44 -0
- data/lib/rspec/graphql_integration/matchers/match_graphql_response.rb +115 -0
- data/lib/rspec/graphql_integration/version.rb +5 -0
- data/lib/rspec/graphql_integration.rb +6 -0
- metadata +105 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 39c4ff53e15be806adbd54539c31cd15f92b7733f6161fbbd3d90b4d827d6d23
|
4
|
+
data.tar.gz: '0780649082510a767cc3bf76225539837a1f0bb4caef92b5f610406b7567d5de'
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 26c78cddba206449f656a17f8ec4e0fe4edf6527b1104be5fd490a8e785be56bec083b2f8d21c155c696b201989a0036898c05f59b3576de3f2e0ba1189c2e66
|
7
|
+
data.tar.gz: a6ae1678e9c113928c98c3c45cd3e7ef33aa432cd9a03eb2ceedc80aa350fd8d1e44d2f77417eae5d5925b36ebdc69b386dedae9b89b595b311274ca5ca7e78f
|
@@ -0,0 +1,30 @@
|
|
1
|
+
module RSpec
|
2
|
+
##
|
3
|
+
# This module sets up GraphQL integration testing for RSpec.
|
4
|
+
module GraphqlIntegration
|
5
|
+
def self.initialize_configuration(config) # rubocop:disable Metrics/MethodLength
|
6
|
+
config.add_setting(:graphql_schema_class, default: nil)
|
7
|
+
|
8
|
+
config.include(RSpec::GraphqlIntegration::Matchers::DeepEq, type: :graphql)
|
9
|
+
config.include(RSpec::GraphqlIntegration::Matchers::MatchGraphqlResponse, type: :graphql)
|
10
|
+
|
11
|
+
config.instance_exec do
|
12
|
+
# This method is inspired by the one from the RSpec::Rails gem.
|
13
|
+
# See https://github.com/rspec/rspec-rails/blob/main/lib/rspec/rails/configuration.rb
|
14
|
+
# => #infer_spec_type_from_file_location!
|
15
|
+
#
|
16
|
+
# It has to be called explicitly in the spec_helper.rb file to be enabled.
|
17
|
+
# See the README for more information.
|
18
|
+
def infer_graphql_spec_type_from_file_location!
|
19
|
+
escaped_path = Regexp.compile("#{%w[spec graphql].join("[\\\\/]")}[\\/]")
|
20
|
+
|
21
|
+
define_derived_metadata(file_path: escaped_path) do |metadata|
|
22
|
+
metadata[:type] ||= :graphql
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
initialize_configuration RSpec.configuration
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
module RSpec
|
2
|
+
module GraphqlIntegration
|
3
|
+
module Matchers
|
4
|
+
##
|
5
|
+
# This matcher recursively compares nested Ruby Hashes and Arrays while ignoring
|
6
|
+
# the order of elements in Arrays.
|
7
|
+
module DeepEq
|
8
|
+
extend RSpec::Matchers::DSL
|
9
|
+
|
10
|
+
matcher(:deep_eq) { |expected| match { |actual| deep_eq?(actual, expected) } }
|
11
|
+
|
12
|
+
def deep_eq?(actual, expected)
|
13
|
+
return arrays_deep_eq?(actual, expected) if expected.is_a?(Array) && actual.is_a?(Array)
|
14
|
+
|
15
|
+
return hashes_deep_eq?(actual, expected) if expected.is_a?(Hash) && actual.is_a?(Hash)
|
16
|
+
|
17
|
+
expected == actual
|
18
|
+
end
|
19
|
+
|
20
|
+
def arrays_deep_eq?(actual, expected)
|
21
|
+
expected = expected.clone
|
22
|
+
|
23
|
+
actual.each do |array|
|
24
|
+
index = expected.find_index { |element| deep_eq?(array, element) }
|
25
|
+
return false if index.nil?
|
26
|
+
|
27
|
+
expected.delete_at(index)
|
28
|
+
end
|
29
|
+
|
30
|
+
expected.empty?
|
31
|
+
end
|
32
|
+
|
33
|
+
def hashes_deep_eq?(actual, expected)
|
34
|
+
return false if actual.keys.sort != expected.keys.sort
|
35
|
+
|
36
|
+
# TODO: use any or all
|
37
|
+
actual.each { |key, value| return false unless deep_eq?(value, expected[key]) }
|
38
|
+
|
39
|
+
true
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,115 @@
|
|
1
|
+
module RSpec
|
2
|
+
module GraphqlIntegration
|
3
|
+
module Matchers
|
4
|
+
##
|
5
|
+
# This module contains the matchers that are used to test GraphQL queries and mutations.
|
6
|
+
module MatchGraphqlResponse
|
7
|
+
extend RSpec::Matchers::DSL
|
8
|
+
|
9
|
+
##
|
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)
|
14
|
+
super <<~TEXT
|
15
|
+
Test variable #{variable_name} is missing.
|
16
|
+
|
17
|
+
Please define it, e.g. with:
|
18
|
+
let(:#{variable_name}) { #{example_value} }
|
19
|
+
TEXT
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
##
|
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
|
29
|
+
|
30
|
+
matcher(:match_graphql_response) do |_expected| # rubocop:disable Metrics/BlockLength
|
31
|
+
match do |_actual|
|
32
|
+
check_variables!
|
33
|
+
|
34
|
+
# We need to test the responses with be_deep_equal so that we ignore
|
35
|
+
# 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
|
40
|
+
end
|
41
|
+
|
42
|
+
# For the failure message, we want to show the diff between the actual and
|
43
|
+
# the expected response and the standard eq matcher from RSpec does that best.
|
44
|
+
failure_message { expect(actual_response).to eq(expected_response) }
|
45
|
+
|
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)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def expected_response
|
55
|
+
expected_response = load_response(test_dir, response_template, response_variables)
|
56
|
+
|
57
|
+
expected_response = [expected_response] unless expected_response.is_a?(Array)
|
58
|
+
|
59
|
+
expected_response
|
60
|
+
end
|
61
|
+
|
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
|
71
|
+
end
|
72
|
+
|
73
|
+
def schema_class
|
74
|
+
# It's possible to overwrite the schema class if an app has multiple schemas.
|
75
|
+
return schema_class_overwrite if defined?(schema_class_overwrite)
|
76
|
+
|
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
|
80
|
+
|
81
|
+
RSpec.configuration.graphql_schema_class
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
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
|
+
##
|
95
|
+
# Loads a response in a JSON file and substitute the passed variables that
|
96
|
+
# are surrounded by {{...}} in the file.
|
97
|
+
#
|
98
|
+
# Example:
|
99
|
+
# load_response(
|
100
|
+
# __FILE__,
|
101
|
+
# 'current_user/response.json',
|
102
|
+
# {
|
103
|
+
# user_id: user.id,
|
104
|
+
# },
|
105
|
+
# )
|
106
|
+
def load_response(dir, filename, variables = {})
|
107
|
+
json_file = File.read(File.join(File.dirname(dir), filename))
|
108
|
+
variables.each { |key, value| json_file.gsub!("\"{{#{key}}}\"", JSON.dump(value)) }
|
109
|
+
|
110
|
+
JSON.parse(json_file)
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
metadata
ADDED
@@ -0,0 +1,105 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: rspec-graphql-integration
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Peter Gundel
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2022-12-31 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: graphql
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 1.0.0
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: 1.0.0
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rspec-core
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: 3.0.0
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: 3.0.0
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: prettier
|
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'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: rubocop
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
description:
|
70
|
+
email:
|
71
|
+
- gundel.peter@gmail.com
|
72
|
+
executables: []
|
73
|
+
extensions: []
|
74
|
+
extra_rdoc_files: []
|
75
|
+
files:
|
76
|
+
- lib/rspec/graphql_integration.rb
|
77
|
+
- lib/rspec/graphql_integration/configuration.rb
|
78
|
+
- lib/rspec/graphql_integration/matchers/deep_eq.rb
|
79
|
+
- lib/rspec/graphql_integration/matchers/match_graphql_response.rb
|
80
|
+
- lib/rspec/graphql_integration/version.rb
|
81
|
+
homepage: https://github.com/peterfication/rspec-graphql-integration
|
82
|
+
licenses:
|
83
|
+
- MIT
|
84
|
+
metadata:
|
85
|
+
rubygems_mfa_required: 'true'
|
86
|
+
post_install_message:
|
87
|
+
rdoc_options: []
|
88
|
+
require_paths:
|
89
|
+
- lib
|
90
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
91
|
+
requirements:
|
92
|
+
- - ">="
|
93
|
+
- !ruby/object:Gem::Version
|
94
|
+
version: '2.6'
|
95
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
96
|
+
requirements:
|
97
|
+
- - ">="
|
98
|
+
- !ruby/object:Gem::Version
|
99
|
+
version: '0'
|
100
|
+
requirements: []
|
101
|
+
rubygems_version: 3.3.7
|
102
|
+
signing_key:
|
103
|
+
specification_version: 4
|
104
|
+
summary: An RSpec plugin to simplify integration tests for GraphQL
|
105
|
+
test_files: []
|