Skip to content

Commit

Permalink
Merge pull request #6 from marcgreenstock/master
Browse files Browse the repository at this point in the history
added interface matcher
  • Loading branch information
khamusa authored Jul 25, 2017
2 parents 0c9ebe3 + f922ccc commit 9aab8b8
Show file tree
Hide file tree
Showing 4 changed files with 134 additions and 2 deletions.
20 changes: 18 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ gem 'rspec-graphql_matchers'

The matchers currently supported are:
- `expect(a_graphql_object).to have_a_field(field_name).that_returns(valid_type)`
- `expect(a_graphql_object).to interface(interface_name, ...)`
- `expect(a_mutation_type).to have_a_return_field(field_name).returning(valid_type)`
- `expect(a_mutation_type).to have_an_input_field(field_name).of_type(valid_type)`
- `expect(a_field).to be_of_type(valid_type)`
Expand All @@ -33,6 +34,8 @@ PostType = GraphQL::ObjectType.define do
name "Post"
description "A blog post"

interfaces [GraphQL::Relay::Node.interface]

field :id, !types.ID

field :comments, !types[types.String]
Expand Down Expand Up @@ -127,6 +130,20 @@ describe PostType do
end
```

### 4) Test an object's interface implementations:

```ruby
describe PostType do
it 'interfaces Node' do
expect(subject).to implement('Node')
end

# Accepts arguments as an array and type objects directly
it { is_expected.to implement(GraphQL::Relay::Node.interface) }
it { is_expected.not_to implement('OtherInterface') }
end
```

The spec will only pass if all attributes/types specified in the hash are
defined on the field.

Expand All @@ -151,6 +168,5 @@ contributors are expected to adhere to the

## License

The gem is available as open source under the terms of the
The gem is available as open source under the terms of the
[MIT License](http://opensource.org/licenses/MIT).

46 changes: 46 additions & 0 deletions lib/rspec/graphql_matchers/implement.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
require_relative 'base_matcher'

module RSpec
module GraphqlMatchers
class Implement < BaseMatcher
def initialize(interface_names)
@expected = interface_names.map(&:to_s)
end

def matches?(graph_object)
@graph_object = graph_object
@actual = actual
@expected.each do |name|
return false unless @actual.include?(name)
end
end

def failure_message
message = "expected interfaces: #{@expected.join(', ')}\n"
message += "actual interfaces: #{@actual.join(', ')}"
message
end

def failure_message_when_negated
message = "unexpected interfaces: #{@expected.join(', ')}\n"
message += "actual interfaces: #{@actual.join(', ')}"
message
end

def description
"implement #{@expected.join(', ')}"
end

private

def actual
if @graph_object.respond_to?(:interfaces)
@graph_object.interfaces.map(&:to_s)
else
raise "Invalid object #{@graph_object} provided to #{matcher_name} " \
'matcher. It does not seem to be a valid GraphQL object type.'
end
end
end
end
end
5 changes: 5 additions & 0 deletions lib/rspec/graphql_matchers/matchers.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
require 'rspec/graphql_matchers/be_of_type'
require 'rspec/graphql_matchers/accept_arguments'
require 'rspec/graphql_matchers/have_a_field'
require 'rspec/graphql_matchers/implement'

module RSpec
module Matchers
Expand Down Expand Up @@ -31,5 +32,9 @@ def have_a_return_field(field_name)
RSpec::GraphqlMatchers::HaveAField.new(field_name, :return_fields)
end
alias have_return_field have_a_return_field

def implement(*interface_names)
RSpec::GraphqlMatchers::Implement.new(interface_names.flatten)
end
end
end
65 changes: 65 additions & 0 deletions spec/rspec/implement_matcher_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
require 'spec_helper'

module RSpec::GraphqlMatchers
describe 'expect(a_type).to implement(interface_names)' do
AnInterface = GraphQL::InterfaceType.define do
name 'AnInterface'
end

AnotherInterface = GraphQL::InterfaceType.define do
name 'AnotherInterface'
end

subject(:a_type) do
GraphQL::ObjectType.define do
name 'TestObject'
interfaces [
GraphQL::Relay::Node.interface,
AnInterface
]
end
end

it { is_expected.to implement('Node') }
it { is_expected.to implement('AnInterface') }
it { is_expected.to implement('Node', 'AnInterface') }
it { is_expected.to implement(['Node']) }
it { is_expected.to implement(['AnInterface']) }
it { is_expected.to implement(['Node', 'AnInterface']) }
it { is_expected.to implement(GraphQL::Relay::Node.interface, AnInterface) }
it { is_expected.to implement([GraphQL::Relay::Node.interface, AnInterface]) }

it { is_expected.not_to implement('AnotherInterface') }
it { is_expected.not_to implement(['AnotherInterface']) }
it { is_expected.not_to implement(AnotherInterface) }
it { is_expected.not_to implement([AnotherInterface]) }

it 'fails with a failure message when the type does include the interfaces' do
expect { expect(a_type).to implement('AnotherInterface') }
.to fail_with(
"expected interfaces: AnotherInterface\n" \
'actual interfaces: Node, AnInterface'
)
end

it 'provides a description' do
matcher = implement('Node, AnInterface')
matcher.matches?(a_type)

expect(matcher.description).to eq('implement Node, AnInterface')
end

context 'when an invalid type is passed' do
let(:a_type) { double(to_s: 'InvalidObject') }

it 'fails with a Runtime error' do
expect { expect(a_type).to have_a_field(:id) }
.to raise_error(
RuntimeError,
'Invalid object InvalidObject provided to have_a_field matcher. ' \
'It does not seem to be a valid GraphQL object type.'
)
end
end
end
end

0 comments on commit 9aab8b8

Please sign in to comment.