graphql-coverage 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 0460ae052196100bcc288337d4c214628a6f083d318c28123b452132ac5e6d59
4
+ data.tar.gz: 884dc07d6864682bf2646e2302f13dff6bf132fe6cd8238746a1e8c684285f66
5
+ SHA512:
6
+ metadata.gz: 80b3b3953f3a40e36efc205895a6337c128e80085255142352b2435b7fab245129c68528373199414ca686f0b0c47b3b1044d07c6abc045565837ff519c9a90e
7
+ data.tar.gz: 0c6d597a081d44bd34093c5a874ba7d8a30199ac941eb70474d9ad2e0fe5e4888f611e0abe558745d673d8d68b3cfe93999377ebb5550c6f477fa1f3ca8f20f2
data/.rspec ADDED
@@ -0,0 +1,3 @@
1
+ --format documentation
2
+ --color
3
+ --require spec_helper
data/.rubocop.yml ADDED
@@ -0,0 +1,19 @@
1
+ AllCops:
2
+ TargetRubyVersion: 3.0
3
+ NewCops: enable
4
+
5
+ Style:
6
+ Enabled: false
7
+
8
+ Metrics:
9
+ Enabled: false
10
+
11
+ Layout/LineLength:
12
+ Enabled: false
13
+
14
+ Layout/FirstHashElementIndentation:
15
+ Enabled: false
16
+
17
+ Naming/FileName:
18
+ Exclude:
19
+ - spec/exe/graphql-coverage_spec.rb
data/CHANGELOG.md ADDED
@@ -0,0 +1,8 @@
1
+ # CHANGELOG
2
+
3
+ ## v0.1.0 - 2023-12-10
4
+
5
+ * Initial release
6
+ * Calculate coverage of GraphQL queries
7
+ * Support multiple processes
8
+ * Support `ignored_fields` option
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2023 Masataka Pocke Kuwabara
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,129 @@
1
+ # GraphQL::Coverage
2
+
3
+ `GraphQL::Coverage` is a gem that calculates the coverage of GraphQL queries.
4
+
5
+ ## Motivation
6
+
7
+ The motivation of this gem is to enforce the coverage of GraphQL queries in testing.
8
+
9
+ In our projects, we have a rule that the test cases must cover all GraphQL fields. However, it is difficult to enforce this rule because we have no tools to check the coverage of GraphQL fields.
10
+
11
+ You may think that we can check the coverage of GraphQL fields with ordinary coverage tools such as simplecov. However, it is not enough because GraphQL fields are often defined without method definition. For example:
12
+
13
+ ```ruby
14
+ # without `def id`
15
+ field :id, String, null: false
16
+ ```
17
+
18
+ So I need to develop a tool that can check the coverage of GraphQL fields.
19
+
20
+ ## Installation
21
+
22
+ Install the gem and add to the application's Gemfile by executing:
23
+
24
+ ```console
25
+ $ bundle add graphql-coverage --require false --group test
26
+ ```
27
+
28
+ If bundler is not being used to manage dependencies, install the gem by executing:
29
+
30
+ ```console
31
+ $ gem install graphql-coverage
32
+ ```
33
+
34
+ ## Usage
35
+
36
+ **This gem is not designed to be used in a production environment.**
37
+
38
+ This gem is designed to be used in a test environment. Here is an example of using this gem with RSpec.
39
+
40
+ ### On a single process
41
+
42
+ If your RSpec runs on a single process, add the following code to `spec_helper.rb`:
43
+
44
+ ```ruby
45
+ RSpec.configure do |config|
46
+ config.before(:suite) do
47
+ require 'graphql/coverage'
48
+ # Pass a class that inherits `GraphQL::Schema`.
49
+ GraphQL::Coverage.enable(YourSchema)
50
+ end
51
+
52
+ config.after(:suite) do
53
+ GraphQL::Coverage.report!
54
+ # You can also use `GraphQL::Coverage.report` if you just want to display the report without failure.
55
+ end
56
+ end
57
+ ```
58
+
59
+ ### On multiple processes
60
+
61
+ You run RSpec on multiple processes in many cases. For example, you may use [parallel_tests](https://github.com/grosser/parallel_tests), [test-queue](https://github.com/tmm1/test-queue), or [CircleCI's parallelism](https://circleci.com/docs/parallelism-faster-jobs/).
62
+ In such cases, you need to use `GraphQL::Coverage` in a different way to aggregate coverage results.
63
+
64
+ The following code is an example of using `GraphQL::Coverage` with CircleCI's parallelism on a Rails application.
65
+
66
+ ```ruby
67
+ # spec_helper.rb
68
+ RSpec.configure do |config|
69
+ config.before(:suite) do
70
+ # Pass a class that inherits `GraphQL::Schema`.
71
+ GraphQL::Coverage.enable(YourSchema)
72
+ end
73
+
74
+ config.after(:suite) do
75
+ # Instead of `GraphQL::Coverage.report!`, use `GraphQL::Coverage.dump` to dump the coverage result to a file.
76
+ GraphQL::Coverage.dump(Rails.root.join("tmp/graphql-coverage-#{ENV['CIRCLE_NODE_INDEX']}.json"))
77
+ end
78
+ end
79
+ ```
80
+
81
+ After running RSpec, you can aggregate the coverage results and display the coverage with the following command:
82
+
83
+ ```console
84
+ # It displays the same report as `GraphQL::Coverage.report!`.
85
+ $ graphql-coverage --require ./config/environment.rb tmp/graphql-coverage-*.json
86
+ ```
87
+
88
+ You must specify `--require` (or `-r`) option to load the schema. If you use Rails, you can specify `./config/environment.rb` as the argument of `--require` option.
89
+
90
+ You can also use `--no-fail-on-uncovered` option to display the coverage without failure.
91
+
92
+ ### Configuration
93
+
94
+ You can specify `ignored_fields` option to ignore some fields.
95
+
96
+ ```ruby
97
+ # spec_helper.rb
98
+ RSpec.configure do |config|
99
+ config.before(:suite) do
100
+ GraphQL::Coverage.enable(YourSchema)
101
+ GraphQL::Coverage.ignored_fields = [
102
+ # GraphQL::Coverage does not complain about the coverage of `Article`'s `title` field.
103
+ { type: 'Article', field: 'title' },
104
+ # You can use `*` as a wildcard.
105
+ { type: '*', field: 'id' },
106
+ ]
107
+ end
108
+ end
109
+ ```
110
+
111
+ I recommend specifying the following configuration in most cases to ignore fields for Relay Connection.
112
+
113
+ ```ruby
114
+ GraphQL::Coverage.ignored_fields = [
115
+ { type: '*', field: 'edges' },
116
+ { type: '*', field: 'node' },
117
+ { type: '*', field: 'cursor' },
118
+ ]
119
+ ```
120
+
121
+ ## Development
122
+
123
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
124
+
125
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and the created tag, and push the `.gem` file to [rubygems.org](https://rubygems.org).
126
+
127
+ ## Contributing
128
+
129
+ Bug reports and pull requests are welcome on GitHub at https://github.com/pocke/graphql-coverage.
data/Rakefile ADDED
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "bundler/gem_tasks"
4
+ require "rspec/core/rake_task"
5
+
6
+ RSpec::Core::RakeTask.new(:spec)
7
+
8
+ require "rubocop/rake_task"
9
+
10
+ RuboCop::RakeTask.new
11
+
12
+ task :steep do
13
+ sh 'steep', 'check'
14
+ end
15
+
16
+ task default: %i[rubocop steep spec]
data/Steepfile ADDED
@@ -0,0 +1,8 @@
1
+ D = Steep::Diagnostic
2
+
3
+ target :lib do
4
+ signature "sig"
5
+ check "lib"
6
+
7
+ configure_code_diagnostics(D::Ruby.strict)
8
+ end
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ $LOAD_PATH << File.join(__dir__, "../lib")
4
+
5
+ require 'graphql/coverage'
6
+ require 'graphql/coverage/cli'
7
+
8
+ exit GraphQL::Coverage::CLI.new(stdout: $stdout, stderr: $stderr).run(ARGV)
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ module GraphQL
4
+ module Coverage
5
+ # rubocop:disable Naming/ConstantName
6
+ # @api private
7
+ Call = _ = Struct.new(:type, :field, :result_type, keyword_init: true)
8
+ # rubocop:enable Naming/ConstantName
9
+ class Call
10
+ def self.from_graphql_object(field:, result_type:)
11
+ new(type: field.owner.graphql_name, field: field.graphql_name, result_type: result_type)
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,50 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'optparse'
4
+
5
+ module GraphQL
6
+ module Coverage
7
+ class CLI
8
+ def initialize(stdout:, stderr:)
9
+ @stdout = stdout
10
+ @stderr = stderr
11
+ end
12
+
13
+ def run(argv)
14
+ # @type var params: params
15
+ params = {
16
+ 'fail-on-uncovered': true,
17
+ }
18
+
19
+ args = OptionParser.new do |opts|
20
+ opts.banner = <<~TXT
21
+ Usage: graphql-coverage [options] [result file paths]
22
+
23
+ Display the coverage result from multiple result files.
24
+
25
+ Options:
26
+ TXT
27
+ opts.version = VERSION
28
+ opts.on('-r', '--require PATH', 'Require a file.') do |path|
29
+ require path
30
+ end
31
+
32
+ opts.on('--[no-]fail-on-uncovered', 'Fail when there are uncovered fields (default: true).')
33
+ end.parse(argv, into: _ = params)
34
+
35
+ Coverage.load(*args)
36
+
37
+ ok = Coverage.report(output: stdout)
38
+ if ok || !params[:'fail-on-uncovered']
39
+ return 0
40
+ else
41
+ 1
42
+ end
43
+ end
44
+
45
+ private
46
+
47
+ attr_reader :stdout, :stderr
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ module GraphQL
4
+ module Coverage
5
+ module Errors
6
+ class SchemaMismatch < StandardError
7
+ def initialize(expected:, got:)
8
+ super("Schema mismatch: expected #{expected}, got #{got}")
9
+ end
10
+ end
11
+
12
+ class UncoveredFields < StandardError
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,55 @@
1
+ # frozen_string_literal: true
2
+
3
+ module GraphQL
4
+ module Coverage
5
+ # @api private
6
+ class Result
7
+ def initialize(calls:, schema:, ignored_fields:)
8
+ @calls = calls.uniq
9
+ @schema = schema
10
+ @ignored_field_patterns = ignored_fields
11
+ end
12
+
13
+ def covered_fields
14
+ @calls
15
+ end
16
+
17
+ def uncovered_fields
18
+ @uncovered_fields ||= reject_ignored_fields(available_fields - @calls)
19
+ end
20
+
21
+ def ignored_fields
22
+ available_fields - uncovered_fields - @calls
23
+ end
24
+
25
+ def available_fields
26
+ # @type var target_types: Array[singleton(GraphQL::Schema::Object)]
27
+ target_types = _ = @schema.types.select { |name, klass| klass < GraphQL::Schema::Object && !name.start_with?('__') }.values
28
+
29
+ target_types.flat_map do |klass|
30
+ klass.fields.values.map do |field|
31
+ Call.from_graphql_object(field: field, result_type: nil)
32
+ end
33
+ end
34
+ end
35
+
36
+ def reject_ignored_fields(calls)
37
+ calls.reject do |call|
38
+ @ignored_field_patterns.any? do |ignored_field|
39
+ type = __skip__ = ignored_field[:type] || ignored_field['type']
40
+ field = __skip__ = ignored_field[:field] || ignored_field['field']
41
+ match_pattern?(type, call.type) && match_pattern?(field, call.field)
42
+ end
43
+ end
44
+ end
45
+
46
+ def match_pattern?(pat, str)
47
+ if pat == '*'
48
+ true
49
+ else
50
+ pat == str
51
+ end
52
+ end
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ module GraphQL
4
+ module Coverage
5
+ # @api private
6
+ class Store
7
+ def self.current
8
+ @current
9
+ end
10
+
11
+ def self.reset!
12
+ @current = new
13
+ end
14
+
15
+ attr_reader :calls
16
+
17
+ def initialize
18
+ @calls = []
19
+ end
20
+
21
+ def append(call)
22
+ @calls << call
23
+ end
24
+
25
+ reset!
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ module GraphQL
4
+ module Coverage
5
+ # @api private
6
+ module Trace
7
+ def execute_field(field:, query:, ast_node:, arguments:, object:)
8
+ result = super
9
+ # TODO: result type
10
+ Store.current.append(Call.from_graphql_object(field: field, result_type: nil))
11
+ result
12
+ end
13
+
14
+ def execute_field_lazy(field:, query:, ast_node:, arguments:, object:)
15
+ result = super
16
+ # TODO: result type
17
+ Store.current.append(Call.from_graphql_object(field: field, result_type: nil))
18
+ result
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ module GraphQL
4
+ module Coverage
5
+ VERSION = "0.1.0"
6
+ end
7
+ end
@@ -0,0 +1,99 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'graphql'
4
+ require 'json'
5
+
6
+ require_relative "coverage/version"
7
+ require_relative "coverage/trace"
8
+ require_relative "coverage/store"
9
+ require_relative "coverage/call"
10
+ require_relative "coverage/errors"
11
+ require_relative "coverage/result"
12
+
13
+ module GraphQL
14
+ module Coverage
15
+ class << self
16
+ attr_accessor :ignored_fields
17
+ end
18
+
19
+ def self.enable(schema)
20
+ self.schema = schema
21
+ schema.trace_with(Trace)
22
+ end
23
+
24
+ def self.dump(file_path)
25
+ calls = Store.current.calls.map(&:to_h)
26
+ content = JSON.generate({ calls: calls, schema: @schema.name, ignored_fields: ignored_fields })
27
+ File.write(_ = file_path, content)
28
+ end
29
+
30
+ def self.load(*file_paths)
31
+ file_paths.each do |file_path|
32
+ content = JSON.parse(File.read(_ = file_path))
33
+ self.schema = Object.const_get(content['schema'])
34
+ self.ignored_fields = content['ignored_fields']
35
+
36
+ content['calls'].each do |call_hash|
37
+ call = Call.new(type: call_hash['type'], field: call_hash['field'], result_type: call_hash['result_type'])
38
+ Store.current.append(call)
39
+ end
40
+ end
41
+ end
42
+
43
+ def self.report(output: $stdout)
44
+ res = result
45
+
46
+ puts_rate = proc do
47
+ available_size = res.available_fields.size
48
+ covered_size = res.covered_fields.size
49
+ ignored_size = res.ignored_fields.size
50
+
51
+ cover_rate = sprintf("%.2f", covered_size.to_f / available_size * 100)
52
+ ignore_rate = sprintf("%.2f", ignored_size.to_f / available_size * 100)
53
+
54
+ output.puts "#{covered_size} / #{available_size} fields covered (#{cover_rate}%)"
55
+ output.puts "#{ignored_size} / #{available_size} fields ignored (#{ignore_rate}%)" if 0 < ignored_size
56
+ end
57
+
58
+ if res.uncovered_fields.empty?
59
+ output.puts "All fields are covered"
60
+ puts_rate.call
61
+ true
62
+ else
63
+ output.puts "There are uncovered fields"
64
+ puts_rate.call
65
+ output.puts "Missing fields:"
66
+ res.uncovered_fields.each do |call|
67
+ output.puts " #{call.type}.#{call.field}"
68
+ end
69
+ false
70
+ end
71
+ end
72
+
73
+ def self.report!(output: $stdout)
74
+ report(output: output) or raise Errors::UncoveredFields
75
+ end
76
+
77
+ def self.result
78
+ Result.new(calls: Store.current.calls, schema: @schema, ignored_fields: ignored_fields)
79
+ end
80
+
81
+ # @api private
82
+ def self.reset!
83
+ __skip__ = @schema = nil
84
+ self.ignored_fields = []
85
+ Store.reset!
86
+ end
87
+
88
+ # @api private
89
+ private_class_method def self.schema=(schema)
90
+ if @schema && @schema != schema
91
+ raise Errors::SchemaMismatch.new(expected: @schema, got: schema)
92
+ end
93
+
94
+ @schema = schema
95
+ end
96
+
97
+ reset!
98
+ end
99
+ end
@@ -0,0 +1,24 @@
1
+ ---
2
+ path: ".gem_rbs_collection"
3
+ gems:
4
+ - name: graphql
5
+ version: '1.12'
6
+ source:
7
+ type: git
8
+ name: ruby/gem_rbs_collection
9
+ revision: 4b0d2f72e63b6c3e92dc54e19ce23dd24524f9a7
10
+ remote: https://github.com/ruby/gem_rbs_collection.git
11
+ repo_dir: gems
12
+ - name: json
13
+ version: '0'
14
+ source:
15
+ type: stdlib
16
+ - name: optparse
17
+ version: '0'
18
+ source:
19
+ type: stdlib
20
+ - name: pathname
21
+ version: '0'
22
+ source:
23
+ type: stdlib
24
+ gemfile_lock_path: Gemfile.lock
@@ -0,0 +1,17 @@
1
+ # Download sources
2
+ sources:
3
+ - type: git
4
+ name: ruby/gem_rbs_collection
5
+ remote: https://github.com/ruby/gem_rbs_collection.git
6
+ revision: main
7
+ repo_dir: gems
8
+
9
+ path: .gem_rbs_collection
10
+
11
+ gems:
12
+ - name: graphql-coverage
13
+ ignore: true
14
+ - name: graphql
15
+ - name: json
16
+ - name: pathname
17
+ - name: optparse
@@ -0,0 +1,14 @@
1
+ module GraphQL
2
+ module Coverage
3
+ # @api private
4
+ class Call
5
+ attr_accessor type: String
6
+ attr_accessor field: String
7
+ attr_accessor result_type: nil
8
+
9
+ def initialize: (type: String, field: String, result_type: nil) -> void
10
+
11
+ def self.from_graphql_object: (field: GraphQL::Schema::Field, result_type: untyped) -> instance
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,19 @@
1
+ module GraphQL
2
+ module Coverage
3
+ # @api private
4
+ class CLI
5
+ type params = {
6
+ :'fail-on-uncovered' => bool,
7
+ }
8
+
9
+ def initialize: (stdout: IO, stderr: IO) -> void
10
+
11
+ def run: (Array[String] argv) -> Integer
12
+
13
+ private
14
+
15
+ attr_reader stdout: IO
16
+ attr_reader stderr: IO
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,14 @@
1
+ module GraphQL
2
+ module Coverage
3
+ module Errors
4
+ # It occurs when the dumped files have different schemas.
5
+ class SchemaMismatch < StandardError
6
+ def initialize: (expected: untyped, got: untyped) -> void
7
+ end
8
+
9
+ # It occurs when the schema is not fully covered by the tests.
10
+ class UncoveredFields < StandardError
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,27 @@
1
+ module GraphQL
2
+ module Coverage
3
+ # @api private
4
+ class Result
5
+ @calls: Array[Call]
6
+ @schema: singleton(GraphQL::Schema)
7
+ @ignored_field_patterns: Array[ignored_field]
8
+ @uncovered_fields: Array[Call]
9
+
10
+ def initialize: (calls: Array[Call], schema: singleton(GraphQL::Schema), ignored_fields: Array[ignored_field]) -> void
11
+
12
+ def covered_fields: () -> Array[Call]
13
+
14
+ def uncovered_fields: () -> Array[Call]
15
+
16
+ def ignored_fields: () -> Array[Call]
17
+
18
+ def available_fields: () -> Array[Call]
19
+
20
+ private
21
+
22
+ def reject_ignored_fields: (Array[Call]) -> Array[Call]
23
+
24
+ def match_pattern?: (String, String) -> bool
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,20 @@
1
+ module GraphQL
2
+ module Coverage
3
+ # @api private
4
+ class Store
5
+ self.@current: untyped
6
+
7
+ @calls: untyped
8
+
9
+ def self.current: () -> untyped
10
+
11
+ def self.reset!: () -> untyped
12
+
13
+ attr_reader calls: untyped
14
+
15
+ def initialize: () -> void
16
+
17
+ def append: (untyped call) -> untyped
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,10 @@
1
+ module GraphQL
2
+ module Coverage
3
+ # @api private
4
+ module Trace : GraphQL::Tracing::Trace
5
+ def execute_field: (field: GraphQL::Schema::Field, query: untyped, ast_node: untyped, arguments: untyped, object: untyped) -> untyped
6
+
7
+ alias execute_field_lazy execute_field
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,5 @@
1
+ module GraphQL
2
+ module Coverage
3
+ VERSION: "0.1.0"
4
+ end
5
+ end
@@ -0,0 +1,39 @@
1
+ module GraphQL
2
+ module Coverage
3
+ type ignored_field = { type: String, field: String }
4
+ | { "type" => String, "field" => String }
5
+
6
+ # Specify fields to ignore in the coverage report.
7
+ # default: []
8
+ attr_accessor self.ignored_fields: Array[ignored_field]
9
+
10
+ self.@schema: singleton(GraphQL::Schema)
11
+
12
+ # Enable coverage tracking for the given schema.
13
+ def self.enable: (singleton(GraphQL::Schema) schema) -> void
14
+
15
+ # Dump the current coverage data to a file.
16
+ def self.dump: (String | Pathname file_path) -> void
17
+
18
+ # Load coverage data from specified files.
19
+ def self.load: (*String | Pathname file_paths) -> void
20
+
21
+ # Report coverage result to the `output`.
22
+ #
23
+ # It reports to the `STDOUT` by default.
24
+ # It returns `true` if the coverage is 100%.
25
+ def self.report: (?output: IO) -> bool
26
+
27
+ # Same as `report` but it raises an error if the coverage is not 100%.
28
+ def self.report!: (?output: IO) -> void
29
+
30
+ # @api private
31
+ def self.result: () -> Result
32
+
33
+ # @api private
34
+ def self.reset!: () -> untyped
35
+
36
+ # @api private
37
+ private def self.schema=: (untyped schema) -> untyped
38
+ end
39
+ end
data/sig/shim.rbs ADDED
@@ -0,0 +1,39 @@
1
+ module GraphQL
2
+ interface _GraphQLName
3
+ def graphql_name: () -> String
4
+ end
5
+
6
+ class Schema
7
+ def self.types: (?untyped context) -> Hash[String, Class]
8
+
9
+ def self.trace_with: (Module trace_mod, ?mode: Symbol | Array[Symbol], **untyped) -> void
10
+
11
+ class Field
12
+ attr_accessor owner: Class & _GraphQLName
13
+ attr_reader name: String
14
+
15
+ alias graphql_name name
16
+ end
17
+
18
+ class Member
19
+ module HashFields
20
+ module ObjectMethods
21
+ def fields: (?untyped context) -> Hash[String, Field]
22
+ end
23
+ end
24
+ end
25
+
26
+ class Object < Member
27
+ extend Member::HashFields
28
+ extend Member::HashFields::ObjectMethods
29
+ end
30
+ end
31
+
32
+ module Tracing
33
+ class Trace
34
+ def execute_field: (field: GraphQL::Schema::Field, query: untyped, ast_node: untyped, arguments: untyped, object: untyped) -> untyped
35
+
36
+ def execute_field_lazy: (field: GraphQL::Schema::Field, query: untyped, ast_node: untyped, arguments: untyped, object: untyped) -> untyped
37
+ end
38
+ end
39
+ end
metadata ADDED
@@ -0,0 +1,89 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: graphql-coverage
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Masataka Pocke Kuwabara
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2023-12-10 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: '2'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '2'
27
+ description: Coverage for GraphQL
28
+ email:
29
+ - kuwabara@pocke.me
30
+ executables:
31
+ - graphql-coverage
32
+ extensions: []
33
+ extra_rdoc_files: []
34
+ files:
35
+ - ".rspec"
36
+ - ".rubocop.yml"
37
+ - CHANGELOG.md
38
+ - LICENSE
39
+ - README.md
40
+ - Rakefile
41
+ - Steepfile
42
+ - exe/graphql-coverage
43
+ - lib/graphql/coverage.rb
44
+ - lib/graphql/coverage/call.rb
45
+ - lib/graphql/coverage/cli.rb
46
+ - lib/graphql/coverage/errors.rb
47
+ - lib/graphql/coverage/result.rb
48
+ - lib/graphql/coverage/store.rb
49
+ - lib/graphql/coverage/trace.rb
50
+ - lib/graphql/coverage/version.rb
51
+ - rbs_collection.lock.yaml
52
+ - rbs_collection.yaml
53
+ - sig/graphql/coverage.rbs
54
+ - sig/graphql/coverage/call.rbs
55
+ - sig/graphql/coverage/cli.rbs
56
+ - sig/graphql/coverage/errors.rbs
57
+ - sig/graphql/coverage/result.rbs
58
+ - sig/graphql/coverage/store.rbs
59
+ - sig/graphql/coverage/trace.rbs
60
+ - sig/graphql/coverage/version.rbs
61
+ - sig/shim.rbs
62
+ homepage: https://github.com/pocke/graphql-coverage
63
+ licenses:
64
+ - MIT
65
+ metadata:
66
+ homepage_uri: https://github.com/pocke/graphql-coverage
67
+ source_code_uri: https://github.com/pocke/graphql-coverage
68
+ changelog_uri: https://github.com/pocke/graphql-coverage/blob/master/CHANGELOG.md
69
+ rubygems_mfa_required: 'true'
70
+ post_install_message:
71
+ rdoc_options: []
72
+ require_paths:
73
+ - lib
74
+ required_ruby_version: !ruby/object:Gem::Requirement
75
+ requirements:
76
+ - - ">="
77
+ - !ruby/object:Gem::Version
78
+ version: '3.0'
79
+ required_rubygems_version: !ruby/object:Gem::Requirement
80
+ requirements:
81
+ - - ">="
82
+ - !ruby/object:Gem::Version
83
+ version: '0'
84
+ requirements: []
85
+ rubygems_version: 3.5.0.dev
86
+ signing_key:
87
+ specification_version: 4
88
+ summary: Coverage for GraphQL
89
+ test_files: []