graphql_matchers.rb 3.0 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495
  1. # frozen_string_literal: true
  2. RSpec::Matchers.define :have_graphql_fields do |*_expected|
  3. def expected_field_names
  4. expected.map { |name| GraphQLHelpers.fieldnamerize(name) }
  5. end
  6. match do |klass|
  7. expect(GraphQLHelpers.keys_for_klass(klass)).to contain_exactly(*expected_field_names)
  8. end
  9. failure_message do |klass|
  10. keys = GraphQLHelpers.keys_for_klass(klass)
  11. missing = expected_field_names - keys
  12. extra = keys - expected_field_names
  13. message = []
  14. message << "is missing fields: <#{missing.inspect}>" if missing.any?
  15. message << "contained unexpected fields: <#{extra.inspect}>" if extra.any?
  16. message.join("\n")
  17. end
  18. end
  19. RSpec::Matchers.define :have_graphql_field do |field_name|
  20. match do |klass|
  21. expect(GraphQLHelpers.keys_for_klass(klass)).to include(GraphQLHelpers.fieldnamerize(field_name))
  22. end
  23. end
  24. RSpec::Matchers.define :have_graphql_mutation do |mutation_class|
  25. match do |field|
  26. expect(field).to be_present
  27. expect(field.resolver).to eq(mutation_class)
  28. end
  29. end
  30. RSpec::Matchers.define :have_graphql_arguments do |*expected|
  31. match do |field|
  32. actual = GraphQLHelpers.arguments_for_field(field)
  33. argument_names = expected.map { |name| GraphQLHelpers.fieldnamerize(name) }
  34. expect(actual).to contain_exactly(*argument_names)
  35. end
  36. end
  37. RSpec::Matchers.define :have_graphql_type do |expected|
  38. match do |field|
  39. # `expected.to_graphql` does not work on Array types because they are _not_
  40. # GraphQL:: objects. There seems to be two options:
  41. #
  42. # 1) Grab the return type from the field to compare to `expected`:
  43. # expect(
  44. # field.metadata[:type_class].instance_variable_get('@return_type_expr')
  45. # ).to eq(expected)
  46. #
  47. # or
  48. #
  49. # 2) Run `expected` through the private GraphQL API and call `to_graphql` on
  50. # the result, while still having to grab important field metadata:
  51. # null = field.metadata[:type_class].instance_variable_get('@return_type_null')
  52. # expect(field.type).do eq(
  53. # GraphQL::Schema::Member::BuildType.parse_type([::Types::PokemonType], null: null).to_graphql
  54. # )
  55. #
  56. # Neither option seems very great, but at least with option 2, we compare
  57. # exactly the expectation. Let's do both?
  58. type_class, graphql_type =
  59. case field
  60. when GraphQL::Schema::Field
  61. [field, field.type.to_graphql]
  62. else
  63. [field.metadata[:type_class], field.type]
  64. end
  65. null = type_class.instance_variable_get('@return_type_null')
  66. parsed_type = GraphQL::Schema::Member::BuildType.parse_type(expected, null: null)
  67. parsed_type = parsed_type.to_graphql unless expected.is_a? GraphQL::ScalarType
  68. expect(graphql_type).to eq(parsed_type)
  69. expect(type_class.instance_variable_get('@return_type_expr')).to eq(expected)
  70. end
  71. end
  72. RSpec::Matchers.define :have_graphql_resolver do |expected|
  73. match do |field|
  74. case expected
  75. when Method
  76. expect(field.metadata[:type_class].resolve_proc).to eq(expected)
  77. else
  78. expect(field.metadata[:type_class].resolver).to eq(expected)
  79. end
  80. end
  81. end