From 0615a6091b0da1b0ba86b1adee7b1144dcaded34 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samuel=20Brand=C3=A3o?= Date: Wed, 19 Jun 2019 16:12:57 -0300 Subject: [PATCH] Latest fixes and release preparation --- CHANGELOG.md | 5 +- README.md | 108 ++++++----------- lib/rspec/graphql_matchers/accept_argument.rb | 73 ++++++++++++ lib/rspec/graphql_matchers/have_a_field.rb | 8 -- lib/rspec/graphql_matchers/matchers.rb | 6 +- lib/rspec/graphql_matchers/version.rb | 2 +- rspec-graphql_matchers-1.0.0.pre.0.1.gem | Bin 0 -> 12800 bytes spec/rspec/accept_argument_matcher_spec.rb | 111 ++++++++++++++++++ spec/rspec/accept_arguments_matcher_spec.rb | 6 - spec/rspec/have_a_return_field_spec.rb | 6 +- .../rspec/have_an_input_field_matcher_spec.rb | 6 +- 11 files changed, 238 insertions(+), 93 deletions(-) create mode 100644 lib/rspec/graphql_matchers/accept_argument.rb create mode 100644 rspec-graphql_matchers-1.0.0.pre.0.1.gem create mode 100644 spec/rspec/accept_argument_matcher_spec.rb diff --git a/CHANGELOG.md b/CHANGELOG.md index c281392..8c2a3b4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,11 +16,14 @@ ### Deprecations -- `.with_metadata` and `.with_property` matchers for fields will be removed on the next release. +- `.with_metadata` and `.with_property` matchers for fields will be removed on the next release; +- `.accept_arguments` (plural form) will be removed on the next release; +- `.accept_argument` (singular form) receiving a hash with a single or multiple arguments will no longer be supported, use `accept_argument(name).of_type('MyType')` instead. ### New features - Add support for Class-based type definition api (adds support for graphql-ruby v1.8.x). Please note that `.with_metadata` and `.with_property` support has been kept but will only work on fields defined using the legacy api. +- Implemented `accept_argument(arg_name).of_type('MyType')ยด matcher, which returns much clearer error messages when the arguments are not found on the target type. ### Bug fixes diff --git a/README.md b/README.md index 292eeea..141ec16 100644 --- a/README.md +++ b/README.md @@ -14,18 +14,17 @@ 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 have_a_field(field_name).of_type(valid_type)` - `expect(a_graphql_object).to implement(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)` -- `expect(an_input).to accept_arguments(hash_of_arg_names_and_valid_types)` -- `expect(an_input).to accept_arguments(hash_of_arg_names_and_valid_types)` +- `expect(an_input).to accept_argument(argument_name).of_type(valid_type)` Where a valid type for the expectation is either: - A reference to the actual type you expect; -- A String representation of a type: `"String!"`, `"Int!"`, `"[String]!"` +- [Recommended] A String representation of a type: `"String!"`, `"Int!"`, `"[String]!"` (note the exclamation mark at the end, as required by the [GraphQL specs](http://graphql.org/). For objects defined with the legacy `#define` api, testing `:property`, `:hash_key` and _metadata_ is also possible by chaining `.with_property`, `.with_hash_key` and `.with_metadata`. For example: @@ -35,31 +34,22 @@ For objects defined with the legacy `#define` api, testing `:property`, `:hash_k ## Examples -Given a `GraphQL::ObjectType` defined as +Given a GraphQL object defined as ```ruby - -PostType = GraphQL::ObjectType.define do - name "Post" +class PostType < GraphQL::Schema::Object + graphql_name "Post" description "A blog post" - interfaces [GraphQL::Relay::Node.interface] - - field :id, !types.ID, - property: :post_id + implements GraphQL::Relay::Node.interface - field :comments, - !types[types.String], - hash_key: :post_comments + field :id, ID, null: false + field :comments, [String], null: false + field :isPublished, Boolean, null: true - field :isPublished, - admin_only: true - - field :subposts, PostType do - type !PostType - - argument :filter, types.String - argument :id, types.ID + field :subposts, PostType, null: true do + argument :filter, types.String, required: false + argument :id, types.ID, required: false end end ``` @@ -68,13 +58,11 @@ end ```ruby describe PostType do - it 'defines a field id of type ID!' do - expect(subject).to have_field(:id).that_returns(!types.ID) - end + subject { described_class } - # Fluent alternatives - it { is_expected.to have_field(:id).of_type("ID!") } - it { is_expected.to have_a_field(:id).returning("ID!") } + it { is_expected.to have_field(:id).of_type(!types.ID) } + it { is_expected.to have_field(:comments).of_type("[String!]!") } + it { is_expected.to have_field(:isPublished).of_type("Boolean") } end ``` @@ -92,80 +80,54 @@ describe PostType do describe 'subposts' do subject { PostType.fields['subposts'] } - # You can use your type object directly when building expectations - it 'has type PostType' do - expect(subject).to be_of_type(!PostType) - end - - # Or as usual, a literal String - it { is_expected.to be_of_type('Post!') } + it { is_expected.to be_of_type('Post') } end end ``` -Keep in mind that when using strings as type expectation you have to use the -type name (`Post`) and not the constant name (`PostType`). - -Using your type objects directly is riskier than the string representation, since -renaming the graphql name of an object is potentially a breaking change that -wouldn't get caught by your test suite. - -You can also use the built-in [graphql-ruby](https://github.com/rmosolgo/graphql-ruby) scalar types: +### 3) For objects defined using the legacy `#define` api, you can also use `with_property`, `with_hash_key` and `with_metadata`: ```ruby -# ensure you have the GraphQL type definer available in your tests -types = GraphQL::Define::TypeDefiner.instance +PostTypeWithDefineApi = GraphQL::ObjectType.define do + name "DefinedPost" -describe PostType do - describe 'comments' do - subject { PostType.fields['comments'] } - it { is_expected.to be_of_type(!types[types.String]) } - it { is_expected.to be_of_type('[String]!') } - end -end -``` + interfaces [GraphQL::Relay::Node.interface] -### 3) Test a specific field with `with_property`, `with_hash_key` and `with_metadata` + field :id, !types.ID, property: :post_id + field :comments, !types[types.String], hash_key: :post_comments + field :isPublished, admin_only: true +end -```ruby -describe PostType do - it { is_expected.to have_a_field(:id).with_property(:post_id) } +describe PostTypeWithDefineApi do + it { is_expected.to have_a_field(:id).of_type('ID!').with_property(:post_id) } it { is_expected.to have_a_field(:comments).with_hash_key(:post_comments) } it { is_expected.to have_a_field(:isPublished).with_metadata(admin_only: true) } end ``` -### 4) Test the arguments accepted by a field with `accept_arguments` matcher: +### 4) Test the arguments accepted by a field with `accept_argument` matcher: ```ruby describe PostType do describe 'subposts' do subject { PostType.fields['subposts'] } - let(:a_whole_bunch_of_args) do - { filter: 'String', id: types.Int, pippo: 'Float', posts: PostType } - end - it 'accepts a filter and an id argument, of types String and ID' do - expect(subject).to accept_arguments(filter: types.String, id: types.ID) + expect(subject).to accept_argument(:filter).of_type('String') + expect(subject).to accept_argument(:id).of_type('ID') end - # You can also test if a field does not accept args. Not quite useful :D. - it { is_expected.not_to accept_arguments(a_whole_bunch_of_args) } + it { is_expected.not_to accept_argument(:weirdo) } end end ``` -The spec will only pass if all attributes/types specified in the hash are -defined on the field. - -For better fluency, `accept_arguments` is also available in singular form, as -`accept_argument`. - ### 5) Test an object's interface implementations: ```ruby describe PostType do + subject { described_class } + it 'implements interface Node' do expect(subject).to implement('Node') end @@ -178,6 +140,8 @@ end ## TODO +- Support GraphQL 1.9.x; +- Check the method used for resolving a field; - New matchers! ## Contributing diff --git a/lib/rspec/graphql_matchers/accept_argument.rb b/lib/rspec/graphql_matchers/accept_argument.rb new file mode 100644 index 0000000..e976b8b --- /dev/null +++ b/lib/rspec/graphql_matchers/accept_argument.rb @@ -0,0 +1,73 @@ +require_relative 'base_matcher' +require_relative './have_a_field_matchers/of_type' + +module RSpec + module GraphqlMatchers + class AcceptArgument < BaseMatcher + def initialize(expected_arg_name) + @expectations = [] + + if expected_arg_name.is_a?(Hash) + (expected_arg_name, expected_type) = expected_arg_name.to_a.first + of_type(expected_type) + + warn 'DEPRECATION WARNING: using accept_arguments with a hash will be '\ + 'deprecated on the next major release. Use the format ' \ + "`accept_argument(:argument_name).of_type('ExpectedType!') instead.`" + end + + @expected_arg_name = expected_arg_name.to_s + end + + def matches?(graph_object) + @graph_object = graph_object + + @actual_argument = field_arguments[@expected_arg_name] + return false if @actual_argument.nil? + + @results = @expectations.select do |matcher| + !matcher.matches?(@actual_argument) + end + + @results.empty? + end + + def of_type(expected_field_type) + @expectations << HaveAFieldMatchers::OfType.new(expected_field_type) + self + end + + def failure_message + base_msg = "expected #{member_name(@graph_object)} " \ + "to accept argument `#{@expected_arg_name}`" \ + + return "#{base_msg} #{failure_messages.join(', ')}" if @actual_argument + + "#{base_msg} but no argument was found with that name" + end + + def description + ["accept argument `#{@expected_arg_name}`"].concat(descriptions).join(', ') + end + + private + + def descriptions + @results.map(&:description) + end + + def failure_messages + @results.map(&:failure_message) + end + + def field_arguments + if @graph_object.respond_to?(:arguments) + @graph_object.public_send(:arguments) + else + raise "Invalid object #{@graph_object} provided to accept_argument " \ + 'matcher. It does not seem to be a valid GraphQL object type.' + end + end + end + end +end diff --git a/lib/rspec/graphql_matchers/have_a_field.rb b/lib/rspec/graphql_matchers/have_a_field.rb index 36ebe95..9ed59a4 100644 --- a/lib/rspec/graphql_matchers/have_a_field.rb +++ b/lib/rspec/graphql_matchers/have_a_field.rb @@ -7,18 +7,10 @@ module RSpec module GraphqlMatchers class HaveAField < BaseMatcher - DESCRIPTIONS = { - type: 'of type `%s`', - property: 'reading from the `%s` property', - hash_key: 'reading from the `%s` hash_key', - metadata: 'with metadata `%s`' - }.freeze - def initialize(expected_field_name, fields = :fields) @expected_field_name = expected_field_name.to_s @fields = fields.to_sym @expectations = [] - @descriptions = [] end def matches?(graph_object) diff --git a/lib/rspec/graphql_matchers/matchers.rb b/lib/rspec/graphql_matchers/matchers.rb index 1d4199f..c342425 100644 --- a/lib/rspec/graphql_matchers/matchers.rb +++ b/lib/rspec/graphql_matchers/matchers.rb @@ -1,6 +1,7 @@ require 'rspec/matchers' require 'rspec/graphql_matchers/be_of_type' require 'rspec/graphql_matchers/accept_arguments' +require 'rspec/graphql_matchers/accept_argument' require 'rspec/graphql_matchers/have_a_field' require 'rspec/graphql_matchers/implement' @@ -10,10 +11,13 @@ def be_of_type(expected) RSpec::GraphqlMatchers::BeOfType.new(expected) end + def accept_argument(expected_argument) + RSpec::GraphqlMatchers::AcceptArgument.new(expected_argument) + end + def accept_arguments(expected_args) RSpec::GraphqlMatchers::AcceptArguments.new(expected_args) end - alias accept_argument accept_arguments # rubocop:disable Style/PredicateName def have_a_field(field_name) diff --git a/lib/rspec/graphql_matchers/version.rb b/lib/rspec/graphql_matchers/version.rb index 33b6b24..76c544b 100644 --- a/lib/rspec/graphql_matchers/version.rb +++ b/lib/rspec/graphql_matchers/version.rb @@ -1,5 +1,5 @@ module Rspec module GraphqlMatchers - VERSION = '1.0.0-0.2'.freeze + VERSION = '1.0'.freeze end end diff --git a/rspec-graphql_matchers-1.0.0.pre.0.1.gem b/rspec-graphql_matchers-1.0.0.pre.0.1.gem new file mode 100644 index 0000000000000000000000000000000000000000..3203a57c1cc94155e417c329248b49716a1f86c9 GIT binary patch literal 12800 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 TUv2({z`qdq7Xtr(A@Dx{Y0H 'ID!') + end + + it 'passes when the type defines the field with correct type as ' \ + 'graphql objects' do + expect(a_type).to accept_argument(:id).of_type(types.String) + expect(a_type).to accept_argument('other').of_type('ID!') + end + + it 'fails when the type defines a field of the wrong type' do + expect { expect(a_type).to accept_argument(:id).of_type('String!') } + .to fail_with( + "expected TestObject to accept argument `id` " \ + 'of type `String!`, but it was `String`' + ) + + expect { expect(a_type).to accept_argument('other').of_type(!types.Int) } + .to fail_with( + "expected TestObject to accept argument `other` " \ + 'of type `Int!`, but it was `ID!`' + ) + + expect { expect(a_type).to accept_argument('other' => !types.Int) } + .to fail_with( + "expected TestObject to accept argument `other` " \ + 'of type `Int!`, but it was `ID!`' + ) + end + + context 'when an invalid type is passed' do + let(:a_type) { Hash.new } + + it 'fails with a Runtime error' do + expect { expect(a_type).to accept_argument(:id) } + .to raise_error( + RuntimeError, + 'Invalid object {} provided to accept_argument ' \ + 'matcher. It does not seem to be a valid GraphQL object type.' + ) + end + end + end + end + + context 'using the new class-based api' do + subject(:a_type) do + Class.new(GraphQL::Schema::InputObject) do + graphql_name 'TestObject' + + argument :id, GraphQL::Types::String, required: false + argument :other, GraphQL::Types::ID, required: true + end + end + + include_examples 'accept argument' + end + + context 'with legacy DSL api' do + subject(:a_type) do + GraphQL::InputObjectType.define do + name 'TestObject' + + argument :id, types.String + argument :other, !types.ID + end + end + + include_examples 'accept argument' + end + end +end diff --git a/spec/rspec/accept_arguments_matcher_spec.rb b/spec/rspec/accept_arguments_matcher_spec.rb index 436be55..c832438 100644 --- a/spec/rspec/accept_arguments_matcher_spec.rb +++ b/spec/rspec/accept_arguments_matcher_spec.rb @@ -3,12 +3,6 @@ describe 'expect(a_field).to accept_arguments(arg_name: arg_type, ...)' do RSpec.shared_examples 'accepts arguments' do - it 'can also be used in singular form' do - expect do - expect(field).to accept_argument({}) - end.not_to raise_error - end - describe '#matches?' do context 'when expecting a single argument with type' do let(:expected_args) { { id: !types.ID } } diff --git a/spec/rspec/have_a_return_field_spec.rb b/spec/rspec/have_a_return_field_spec.rb index e1e3d80..8dd0616 100644 --- a/spec/rspec/have_a_return_field_spec.rb +++ b/spec/rspec/have_a_return_field_spec.rb @@ -54,13 +54,15 @@ module RSpec::GraphqlMatchers it 'fails when the type defines a field of the wrong type' do expect { expect(a_type).to have_a_return_field(:id).returning('String!') } .to fail_with( - "expected #{a_type.name} to define field `id` of type `String!`" + "expected #{a_type.name} to define field `id` of type `String!`, " \ + 'but it was `String`' ) expect do expect(a_type).to have_a_return_field('other').returning(!types.Int) end.to fail_with( - "expected #{a_type.name} to define field `other` of type `Int!`" + "expected #{a_type.name} to define field `other` of type `Int!`, " \ + 'but it was `ID!`' ) end diff --git a/spec/rspec/have_an_input_field_matcher_spec.rb b/spec/rspec/have_an_input_field_matcher_spec.rb index a0fb285..7e37aa4 100644 --- a/spec/rspec/have_an_input_field_matcher_spec.rb +++ b/spec/rspec/have_an_input_field_matcher_spec.rb @@ -54,13 +54,15 @@ module RSpec::GraphqlMatchers it 'fails when the type defines a field of the wrong type' do expect { expect(a_type).to have_an_input_field(:id).returning('String!') } .to fail_with( - "expected #{a_type.name} to define field `id` of type `String!`" + "expected #{a_type.name} to define field `id` of type `String!`, " \ + 'but it was `String`' ) expect do expect(a_type).to have_an_input_field('other').returning(!types.Int) end.to fail_with( - "expected #{a_type.name} to define field `other` of type `Int!`" + "expected #{a_type.name} to define field `other` of type `Int!`, " \ + 'but it was `ID!`' ) end