simple_cov-formatter-terminal 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 0037c0f75039602b73a5c02b56091392b253bbd14d297f44fe6a0a1463dfa844
4
+ data.tar.gz: a91aee34a3560e838481318fc56680682b01a3a635ca7f18ae0d83049aeea298
5
+ SHA512:
6
+ metadata.gz: 5bc1c96efcf14e290abdeca72bf850a4762239f8a01760c7db162c7cfb2418093e42c34c2fd4482379a3565b1eec292254b097e5a63a9def8dbf431703e9933b
7
+ data.tar.gz: cc5c94164de7385f4bb770017b657d548aff92e516fbc9fc580b192f2e387b190997118cc11a89460e925a7d6cbc318f165f2fa3df3a3957fd020130d6738427
@@ -0,0 +1,3 @@
1
+ ---
2
+ git: true
3
+ rubygems: true
data/.rspec ADDED
@@ -0,0 +1,3 @@
1
+ --format documentation
2
+ --color
3
+ --require spec_helper
data/.rubocop.yml ADDED
@@ -0,0 +1,5 @@
1
+ inherit_gem:
2
+ runger_style:
3
+ - rulesets/default.yml # gem 'rubocop'
4
+ - rulesets/performance.yml # gem 'rubocop-performance'
5
+ - rulesets/rspec.yml # gem 'rubocop-rspec'
data/CHANGELOG.md ADDED
@@ -0,0 +1,5 @@
1
+ ## [Unreleased]
2
+
3
+ ## [0.1.0] - 2022-10-07
4
+
5
+ - Initial release
data/Gemfile ADDED
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ ruby '3.2.0'
4
+
5
+ source 'https://rubygems.org'
6
+
7
+ git_source(:github) { |repo_name| "https://github.com/#{repo_name}" }
8
+
9
+ # Specify your gem's dependencies in fcom.gemspec
10
+ gemspec
11
+
12
+ group :development, :test do
13
+ gem 'bundler', require: false
14
+ gem 'pry', require: false
15
+ gem 'pry-byebug', require: false
16
+ gem 'rubocop', require: false
17
+ gem 'rubocop-performance', require: false
18
+ gem 'rubocop-rspec', require: false
19
+ gem 'runger_style', github: 'davidrunger/runger_style', require: false
20
+ end
21
+
22
+ group :test do
23
+ gem 'climate_control', require: false
24
+ gem 'codecov', require: false
25
+ gem 'rspec', require: false
26
+ gem 'simplecov', require: false
27
+ end
28
+
29
+ group :development do
30
+ gem 'rake', require: false
31
+ gem 'release_assistant', require: false, git: 'https://github.com/davidrunger/release_assistant'
32
+ end
data/Gemfile.lock ADDED
@@ -0,0 +1,131 @@
1
+ GIT
2
+ remote: https://github.com/davidrunger/release_assistant
3
+ revision: 4a6db008f26327bbee9758ddd2bad070b14d2e88
4
+ specs:
5
+ release_assistant (0.3.3.alpha)
6
+ activesupport (>= 6, < 8)
7
+ colorize (~> 0.8)
8
+ memoist (~> 0.16)
9
+ slop (~> 4.8)
10
+
11
+ GIT
12
+ remote: https://github.com/davidrunger/runger_style
13
+ revision: 4a68671e36d5734cb50331f904fb5a1a7ab9b429
14
+ specs:
15
+ runger_style (0.2.22.alpha)
16
+ rubocop (>= 1.38.0, < 2)
17
+
18
+ PATH
19
+ remote: .
20
+ specs:
21
+ simple_cov-formatter-terminal (0.1.0)
22
+ activesupport (>= 7.0.4, < 8)
23
+ memoist (>= 0.16.2, < 1)
24
+ rouge (>= 4.0.0, < 5)
25
+ rspec-core (>= 3.11.0, < 4)
26
+ simplecov (>= 0.21.2, < 1)
27
+
28
+ GEM
29
+ remote: https://rubygems.org/
30
+ specs:
31
+ activesupport (7.0.4)
32
+ concurrent-ruby (~> 1.0, >= 1.0.2)
33
+ i18n (>= 1.6, < 2)
34
+ minitest (>= 5.1)
35
+ tzinfo (~> 2.0)
36
+ ast (2.4.2)
37
+ byebug (11.1.3)
38
+ climate_control (1.2.0)
39
+ codecov (0.6.0)
40
+ simplecov (>= 0.15, < 0.22)
41
+ coderay (1.1.3)
42
+ colorize (0.8.1)
43
+ concurrent-ruby (1.1.10)
44
+ diff-lcs (1.5.0)
45
+ docile (1.4.0)
46
+ i18n (1.12.0)
47
+ concurrent-ruby (~> 1.0)
48
+ json (2.6.3)
49
+ memoist (0.16.2)
50
+ method_source (1.0.0)
51
+ minitest (5.16.3)
52
+ parallel (1.22.1)
53
+ parser (3.2.0.0)
54
+ ast (~> 2.4.1)
55
+ pry (0.14.1)
56
+ coderay (~> 1.1)
57
+ method_source (~> 1.0)
58
+ pry-byebug (3.10.1)
59
+ byebug (~> 11.0)
60
+ pry (>= 0.13, < 0.15)
61
+ rainbow (3.1.1)
62
+ rake (13.0.6)
63
+ regexp_parser (2.6.1)
64
+ rexml (3.2.5)
65
+ rouge (4.0.0)
66
+ rspec (3.11.0)
67
+ rspec-core (~> 3.11.0)
68
+ rspec-expectations (~> 3.11.0)
69
+ rspec-mocks (~> 3.11.0)
70
+ rspec-core (3.11.0)
71
+ rspec-support (~> 3.11.0)
72
+ rspec-expectations (3.11.1)
73
+ diff-lcs (>= 1.2.0, < 2.0)
74
+ rspec-support (~> 3.11.0)
75
+ rspec-mocks (3.11.1)
76
+ diff-lcs (>= 1.2.0, < 2.0)
77
+ rspec-support (~> 3.11.0)
78
+ rspec-support (3.11.1)
79
+ rubocop (1.42.0)
80
+ json (~> 2.3)
81
+ parallel (~> 1.10)
82
+ parser (>= 3.1.2.1)
83
+ rainbow (>= 2.2.2, < 4.0)
84
+ regexp_parser (>= 1.8, < 3.0)
85
+ rexml (>= 3.2.5, < 4.0)
86
+ rubocop-ast (>= 1.24.1, < 2.0)
87
+ ruby-progressbar (~> 1.7)
88
+ unicode-display_width (>= 1.4.0, < 3.0)
89
+ rubocop-ast (1.24.1)
90
+ parser (>= 3.1.1.0)
91
+ rubocop-performance (1.15.0)
92
+ rubocop (>= 1.7.0, < 2.0)
93
+ rubocop-ast (>= 0.4.0)
94
+ rubocop-rspec (2.13.2)
95
+ rubocop (~> 1.33)
96
+ ruby-progressbar (1.11.0)
97
+ simplecov (0.21.2)
98
+ docile (~> 1.1)
99
+ simplecov-html (~> 0.11)
100
+ simplecov_json_formatter (~> 0.1)
101
+ simplecov-html (0.12.3)
102
+ simplecov_json_formatter (0.1.4)
103
+ slop (4.9.3)
104
+ tzinfo (2.0.5)
105
+ concurrent-ruby (~> 1.0)
106
+ unicode-display_width (2.4.2)
107
+
108
+ PLATFORMS
109
+ ruby
110
+
111
+ DEPENDENCIES
112
+ bundler
113
+ climate_control
114
+ codecov
115
+ pry
116
+ pry-byebug
117
+ rake
118
+ release_assistant!
119
+ rspec
120
+ rubocop
121
+ rubocop-performance
122
+ rubocop-rspec
123
+ runger_style!
124
+ simple_cov-formatter-terminal!
125
+ simplecov
126
+
127
+ RUBY VERSION
128
+ ruby 3.2.0p0
129
+
130
+ BUNDLED WITH
131
+ 2.3.23
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2022 David Runger
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
13
+ all 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
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,123 @@
1
+ # SimpleCov::Formatter::Terminal
2
+
3
+ ![image](https://user-images.githubusercontent.com/8197963/195740768-e2cbb99d-7cf2-42bf-a178-2f78eb653dd3.png)
4
+
5
+ ## Installation
6
+
7
+ *Note:* This gem depends upon RSpec. If you aren't using RSpec to run your tests, it won't work!
8
+
9
+ Add the gem to your application's `Gemfile`. Because the gem is not released via RubyGems, you will
10
+ need to install it from GitHub.
11
+
12
+ ```rb
13
+ group :test do
14
+ gem 'simple_cov-formatter-terminal', github: 'davidrunger/simple_cov-formatter-terminal'
15
+ end
16
+ ```
17
+
18
+ Then execute:
19
+
20
+ ```
21
+ $ bundle install
22
+ ```
23
+
24
+ ## Usage
25
+
26
+ Add something like the following to your `spec/spec_helper.rb` file:
27
+
28
+ ```rb
29
+ require 'simplecov'
30
+ if RSpec.configuration.files_to_run.one?
31
+ require 'simple_cov/formatter/terminal'
32
+ SimpleCov.formatter = SimpleCov::Formatter::Terminal
33
+ end
34
+ SimpleCov.start
35
+ ```
36
+
37
+ **Note** that this setup only uses `SimpleCov::Formatter::Terminal` when specs are run with a single
38
+ spec file (e.g. `bin/rspec spec/models/user_spec.rb`) and not when multiple specs are executed (e.g.
39
+ when simply running `bin/rspec` without any argument).
40
+
41
+ ### Modifying the `spec_to_app_file_map`
42
+
43
+ `SimpleCov::Formatter::Terminal` has a default hash that is used to map spec files to their
44
+ corresponding application file.
45
+
46
+ For gems (determined from whether there is a `.gemspec` file at the top level of the project), the
47
+ default mapping is very simple:
48
+
49
+ ```rb
50
+ SimpleCov::Formatter::Terminal.config.spec_to_app_file_map = {
51
+ %r{\Aspec/} => 'lib/',
52
+ }
53
+ ```
54
+
55
+ This is the default used for non-gems; it is optimized for Rails applications using Active Admin:
56
+
57
+ ```rb
58
+ SimpleCov::Formatter::Terminal.config.spec_to_app_file_map = {
59
+ %r{\Aspec/lib/} => 'lib/',
60
+ %r{\Aspec/controllers/admin/(.*)_controller_spec.rb} => 'app/admin/\1.rb',
61
+ %r{
62
+ \Aspec/
63
+ (
64
+ actions|
65
+ channels|
66
+ controllers|
67
+ decorators|
68
+ helpers|
69
+ mailboxes|
70
+ mailers|
71
+ models|
72
+ policies|
73
+ serializers|
74
+ views|
75
+ workers
76
+ )
77
+ /
78
+ }x => 'app/\1/',
79
+ }
80
+ ```
81
+
82
+ If needed for your application, you can add to this hash in your `spec/spec_helper.rb`, e.g.:
83
+
84
+ ```rb
85
+ SimpleCov::Formatter::Terminal.config.spec_to_app_file_map.merge!(
86
+ %r{\Aspec/my_special/directory_structure/} => 'my_special/app_directory/',
87
+ )
88
+ ```
89
+
90
+ Or you can override the default mapping completely:
91
+
92
+ ```rb
93
+ SimpleCov::Formatter::Terminal.config.spec_to_app_file_map = {
94
+ %r{\Aspec/my_special/directory_structure/} => 'my_special/app_directory/',
95
+ }
96
+ ```
97
+
98
+ ## Branch coverage
99
+
100
+ If you enable branch coverage for SimpleCov (via `enable_coverage(:branch)` within your
101
+ `SimpleCov.start` block; read more [here][simple-cov-branch-coverage]), then branch coverage info
102
+ will be included in SimpleCov::Formatter::Terminal's output. Missed branches will be indicated with
103
+ white text on a red background at the end of the line, and the line number will be printed in red
104
+ text on a yellow background, as seen in the screenshot at the top of this README (where a `then`
105
+ branch is not covered on line 18).
106
+
107
+ [simple-cov-branch-coverage]: https://github.com/simplecov-ruby/simplecov#branch-coverage-ruby--25
108
+
109
+ ## Development
110
+
111
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `bin/rspec` to run
112
+ the tests. You can also run `bin/console` for an interactive prompt that will allow you to
113
+ experiment.
114
+
115
+ ## Contributing
116
+
117
+ Bug reports and pull requests are welcome on GitHub at
118
+ https://github.com/davidrunger/simple_cov-formatter-terminal.
119
+
120
+ ## License
121
+
122
+ The gem is available as open source under the terms of the [MIT
123
+ License](https://opensource.org/licenses/MIT).
data/Rakefile ADDED
@@ -0,0 +1,12 @@
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 default: %i[spec rubocop]
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'memoist'
4
+
5
+ module SimpleCov::Formatter::Terminal::BranchCoverage
6
+ extend Memoist
7
+
8
+ private
9
+
10
+ memoize \
11
+ def uncovered_branches(sourcefile)
12
+ sourcefile.branches.
13
+ reject(&:covered?).
14
+ reject do |branch|
15
+ line = sourcefile.lines[branch.start_line - 1]
16
+ source_code = line.src
17
+ line.coverage == 0 || source_code.match?(/# :nocov-(#{branch.type}):/)
18
+ end
19
+ end
20
+
21
+ def missed_branch_info(line, sourcefile)
22
+ uncovered_branches(sourcefile).
23
+ select { _1.start_line == line.line_number }.
24
+ map { _1.type.to_s }.
25
+ join(', ').
26
+ presence
27
+ end
28
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SimpleCov::Formatter::Terminal::ColorPrinting
4
+ # rubocop:disable Metrics/CyclomaticComplexity
5
+ def color(message, color)
6
+ case color
7
+ when :white then "\e[0;37;49m#{message}\e[0m"
8
+ when :red then "\e[0;31m#{message}\e[0m"
9
+ when :green then "\e[1;32;49m#{message}\e[0m"
10
+ when :yellow then "\e[0;33m#{message}\e[0m"
11
+ when :white_on_green then "\e[1;39;102m#{message}\e[0m"
12
+ when :red_on_yellow then "\e[0;31;103m#{message}\e[0m"
13
+ when :white_on_red then "\e[1;37;41m#{message}\e[0m"
14
+ else raise("Unknown color format '#{color}'.")
15
+ end
16
+ end
17
+ # rubocop:enable Metrics/CyclomaticComplexity
18
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative './spec_to_app_mapping'
4
+ require 'memoist'
5
+
6
+ class SimpleCov::Formatter::Terminal::Config
7
+ extend Memoist
8
+ include SimpleCov::Formatter::Terminal::SpecToAppMapping
9
+
10
+ attr_accessor :spec_to_app_file_map, :unmappable_spec_regexes
11
+
12
+ def initialize
13
+ @spec_to_app_file_map =
14
+ SimpleCov::Formatter::Terminal::SpecToAppMapping.default_spec_to_app_map
15
+ @unmappable_spec_regexes =
16
+ SimpleCov::Formatter::Terminal::SpecToAppMapping::DEFAULT_UNMAPPABLE_SPEC_REGEXES
17
+ end
18
+
19
+ memoize \
20
+ def write_target_info_file?
21
+ ENV.fetch('SIMPLECOV_WRITE_TARGET_TO_FILE', nil) == '1'
22
+ end
23
+ end
@@ -0,0 +1,53 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'memoist'
4
+
5
+ class SimpleCov::Formatter::Terminal::FileDeterminer
6
+ extend Memoist
7
+
8
+ memoize \
9
+ def executed_spec_file
10
+ if executed_spec_files.size == 1
11
+ executed_spec_files.first
12
+ else
13
+ raise(<<~ERROR)
14
+ Multiple spec files were executed (#{executed_spec_files}), but
15
+ SimpleCov::Formatter::Terminal only works when a single spec file is executed.
16
+ ERROR
17
+ end
18
+ end
19
+
20
+ memoize \
21
+ def targeted_application_file
22
+ env_variable_file = ENV.fetch('SIMPLECOV_TARGET_FILE', nil)
23
+ if !env_variable_file.nil?
24
+ puts('Determined targeted application file from SIMPLECOV_TARGET_FILE environment variable!!')
25
+ return env_variable_file
26
+ end
27
+
28
+ return nil if unmappable_spec_regexes.any? { executed_spec_file.match?(_1) }
29
+
30
+ spec_to_app_file_map.lazy.filter_map do |spec_file_regex, app_file_substitution|
31
+ if executed_spec_file.match?(spec_file_regex)
32
+ executed_spec_file.sub(spec_file_regex, app_file_substitution)
33
+ end
34
+ end.first&.sub(/_spec\.rb\z/, '.rb') ||
35
+ raise("Could not map executed spec file #{executed_spec_file} to application file!")
36
+ end
37
+
38
+ memoize \
39
+ def executed_spec_files
40
+ SimpleCov::Formatter::Terminal::RSpecIntegration.executed_spec_files
41
+ end
42
+
43
+ memoize \
44
+ def spec_to_app_file_map
45
+ SimpleCov::Formatter::Terminal.config.spec_to_app_file_map ||
46
+ SimpleCov::Formatter::Terminal::SpecToAppMapping.default_spec_to_app_map
47
+ end
48
+
49
+ memoize \
50
+ def unmappable_spec_regexes
51
+ SimpleCov::Formatter::Terminal.config.unmappable_spec_regexes
52
+ end
53
+ end
@@ -0,0 +1,70 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'memoist'
4
+
5
+ class SimpleCov::Formatter::Terminal::LinePrinter
6
+ extend Memoist
7
+ include SimpleCov::Formatter::Terminal::BranchCoverage
8
+ include SimpleCov::Formatter::Terminal::ColorPrinting
9
+
10
+ def initialize(targeted_application_file)
11
+ @targeted_application_file = targeted_application_file
12
+ end
13
+
14
+ def colored_line(line, sourcefile)
15
+ colored_source_code = syntax_highlighted_source_lines[line.line_number - 1]
16
+ line_number = line.line_number
17
+
18
+ case
19
+ when line.skipped?
20
+ numbered_line_output(line_number, :white, colored_source_code)
21
+ when line.coverage == 0
22
+ numbered_line_output(line_number, :white_on_red, colored_source_code)
23
+ when (missed_branch_info = missed_branch_info(line, sourcefile))
24
+ numbered_line_output(line_number, :red_on_yellow, colored_source_code, missed_branch_info)
25
+ when line.coverage.nil?
26
+ numbered_line_output(line_number, :white, colored_source_code, missed_branch_info)
27
+ else
28
+ numbered_line_output(line_number, :green, colored_source_code, missed_branch_info)
29
+ end
30
+ end
31
+
32
+ # rubocop:disable Style/StringConcatenation
33
+ def numbered_line_output(line_number, color, source_code, missed_branch_info = nil)
34
+ colored_space =
35
+ case color
36
+ when :red_on_yellow, :white_on_red then color(' ', color)
37
+ else color(' ', :white_on_green)
38
+ end
39
+
40
+ line_number_string =
41
+ if SimpleCov::Formatter::Terminal.config.write_target_info_file?
42
+ ":::#{line_number}".rjust(6, ' ')
43
+ else
44
+ line_number.to_s.rjust(3, ' ')
45
+ end
46
+
47
+ output =
48
+ colored_space +
49
+ color(line_number_string, color) +
50
+ colored_space +
51
+ ' ' +
52
+ source_code
53
+
54
+ if missed_branch_info
55
+ output << " #{color(missed_branch_info, :white_on_red)}"
56
+ end
57
+
58
+ output
59
+ end
60
+ # rubocop:enable Style/StringConcatenation
61
+
62
+ memoize \
63
+ def syntax_highlighted_source_lines
64
+ source = File.read(@targeted_application_file)
65
+ formatter = Rouge::Formatters::Terminal256.new(Rouge::Themes::Base16.mode(:dark).new)
66
+ lexer = Rouge::Lexers::Ruby.new
67
+ highlighted_source = formatter.format(lexer.lex(source))
68
+ highlighted_source.split("\n")
69
+ end
70
+ end
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SimpleCov::Formatter::Terminal::RSpecIntegration
4
+ class << self
5
+ attr_accessor :executed_spec_files, :failure_occurred
6
+
7
+ def setup_rspec
8
+ return if @rspec_is_set_up # :nocov-else:
9
+
10
+ # We can't easily test this, since we use this library in its own RSpec tests,
11
+ # so we'd be setting it up twice if we tested it, which would be a bit of a problem.
12
+ # :nocov:
13
+ _setup_rspec
14
+ @rspec_is_set_up = true
15
+ # :nocov:
16
+ end
17
+
18
+ def failure_occurred?
19
+ !!@failure_occurred
20
+ end
21
+
22
+ private
23
+
24
+ def _setup_rspec
25
+ RSpec.configure do |config|
26
+ config.before(:suite) do
27
+ SimpleCov::Formatter::Terminal::RSpecIntegration.failure_occurred = false
28
+ end
29
+
30
+ config.after(:suite) do
31
+ examples = RSpec.world.filtered_examples.values.flatten
32
+ SimpleCov::Formatter::Terminal::RSpecIntegration.failure_occurred =
33
+ examples.any?(&:exception)
34
+ SimpleCov::Formatter::Terminal::RSpecIntegration.executed_spec_files =
35
+ examples.map { _1.file_path.delete_prefix('./') }.uniq
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SimpleCov::Formatter::Terminal::RailsAwareness
4
+ class << self
5
+ def rails?
6
+ !!defined?(Rails)
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,127 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative './branch_coverage'
4
+ require_relative './color_printing'
5
+ require_relative './line_printer'
6
+ require_relative './target_file_writer'
7
+
8
+ class SimpleCov::Formatter::Terminal::ResultPrinter
9
+ extend Forwardable
10
+ extend Memoist
11
+ include SimpleCov::Formatter::Terminal::BranchCoverage
12
+ include SimpleCov::Formatter::Terminal::ColorPrinting
13
+
14
+ def_delegators(:@file_determiner, :executed_spec_file, :targeted_application_file)
15
+
16
+ def initialize(file_determiner)
17
+ @file_determiner = file_determiner
18
+ end
19
+
20
+ def print_coverage_info(result)
21
+ sourcefile = result.files.find { _1.filename.end_with?(targeted_application_file) }
22
+ force_coverage = ENV.fetch('SIMPLECOV_FORCE_DETAILS', nil) == '1'
23
+
24
+ if sourcefile.nil?
25
+ print_no_coverage_info_found
26
+ elsif failure_occurred? && !force_coverage
27
+ print_coverage_summary(sourcefile, 'Not showing detailed coverage because an example failed.')
28
+ elsif sourcefile.covered_percent < 100 || uncovered_branches(sourcefile).any? || force_coverage
29
+ print_coverage_details(sourcefile)
30
+ else
31
+ print_coverage_summary(sourcefile)
32
+ end
33
+ end
34
+
35
+ def print_coverage_summary(sourcefile, log_addendum = nil)
36
+ summary = +"-- Coverage for #{targeted_application_file} --\n"
37
+ summary << "Line coverage: #{colorized_coverage(sourcefile.covered_percent)}"
38
+ if SimpleCov.branch_coverage?
39
+ summary << ' '
40
+ summary << <<~LOG
41
+ | Uncovered branches: #{colorized_uncovered_branches(uncovered_branches(sourcefile).size)}
42
+ LOG
43
+ else
44
+ summary << "\n"
45
+ end
46
+ summary << log_addendum if log_addendum
47
+ puts(summary)
48
+ end
49
+
50
+ def print_coverage_details(sourcefile)
51
+ if SimpleCov::Formatter::Terminal.config.write_target_info_file?
52
+ target_file_writer.write_target_info_file
53
+ end
54
+
55
+ puts("---- Coverage for #{targeted_application_file} ".ljust(80, '-').rstrip)
56
+ sourcefile.lines.each do |line|
57
+ puts(line_printer.colored_line(line, sourcefile))
58
+ end
59
+ puts(<<~LOG.squish)
60
+ ----
61
+ Line coverage: #{colorized_coverage(sourcefile.covered_percent)}
62
+ |
63
+ Uncovered branches: #{colorized_uncovered_branches(uncovered_branches(sourcefile).size)}
64
+ ----
65
+ LOG
66
+ end
67
+
68
+ def print_no_coverage_info_found
69
+ puts(<<~LOG.squish)
70
+ No code coverage info was found for "#{targeted_application_file}". Try stopping and
71
+ disabling `spring`, if you are using it, and then rerun the spec.
72
+ LOG
73
+ end
74
+
75
+ def print_info_for_no_executed_specs
76
+ puts('Not showing test coverage details because no specs were executed successfully.')
77
+ end
78
+
79
+ def print_info_for_undetermined_application_target
80
+ puts(<<~LOG.squish)
81
+ Not showing test coverage details because "#{executed_spec_file}" cannot
82
+ be mapped to a single application file.
83
+ LOG
84
+ puts(<<~LOG.squish)
85
+ Tip: you can specify a file manually via a SIMPLECOV_TARGET_FILE environment variable.
86
+ LOG
87
+ end
88
+
89
+ def print_info_for_nonexistent_application_target
90
+ puts(<<~LOG.squish)
91
+ Cannot show code coverage. Looked for application file "#{targeted_application_file}",
92
+ but it does not exist.
93
+ LOG
94
+ end
95
+
96
+ private
97
+
98
+ def failure_occurred?
99
+ SimpleCov::Formatter::Terminal::RSpecIntegration.failure_occurred?
100
+ end
101
+
102
+ memoize \
103
+ def target_file_writer
104
+ SimpleCov::Formatter::Terminal::TargetFileWriter.new(targeted_application_file)
105
+ end
106
+
107
+ def colorized_coverage(covered_percent)
108
+ case
109
+ when covered_percent >= 100 then color("#{covered_percent.round(2)}%", :green)
110
+ when covered_percent >= 80 then color("#{covered_percent.round(2)}%", :yellow)
111
+ else color("#{covered_percent.round(2)}%", :red)
112
+ end
113
+ end
114
+
115
+ def colorized_uncovered_branches(num_uncovered_branches)
116
+ case num_uncovered_branches
117
+ when 0 then color(num_uncovered_branches.to_s, :green)
118
+ when (1..3) then color(num_uncovered_branches.to_s, :yellow)
119
+ else color(num_uncovered_branches.to_s, :red)
120
+ end
121
+ end
122
+
123
+ memoize \
124
+ def line_printer
125
+ SimpleCov::Formatter::Terminal::LinePrinter.new(targeted_application_file)
126
+ end
127
+ end
@@ -0,0 +1,47 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative './rails_awareness'
4
+
5
+ module SimpleCov::Formatter::Terminal::SpecToAppMapping
6
+ # rubocop:disable Lint/OrAssignmentToConstant
7
+ DEFAULT_UNMAPPABLE_SPEC_REGEXES ||= [
8
+ %r{\Aspec/features/},
9
+ ].freeze
10
+ SPEC_TO_GEM_DEFAULT_MAP ||= {
11
+ %r{\Aspec/} => 'lib/',
12
+ }.freeze
13
+ SPEC_TO_RAILS_DEFAULT_MAP ||= {
14
+ %r{\Aspec/lib/} => 'lib/',
15
+ %r{\Aspec/controllers/admin/(.*)_controller_spec.rb} => 'app/admin/\1.rb',
16
+ %r{
17
+ \Aspec/
18
+ (
19
+ actions|
20
+ channels|
21
+ controllers|
22
+ decorators|
23
+ helpers|
24
+ mailboxes|
25
+ mailers|
26
+ models|
27
+ policies|
28
+ serializers|
29
+ views|
30
+ workers
31
+ )
32
+ /
33
+ }x => 'app/\1/',
34
+ }.freeze
35
+ # rubocop:enable Lint/OrAssignmentToConstant
36
+
37
+ class << self
38
+ def default_spec_to_app_map
39
+ # dup the maps because the maps are frozen but we want to allow the user to customize them
40
+ if SimpleCov::Formatter::Terminal::RailsAwareness.rails?
41
+ SPEC_TO_RAILS_DEFAULT_MAP.dup
42
+ else
43
+ SPEC_TO_GEM_DEFAULT_MAP.dup
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'memoist'
4
+
5
+ class SimpleCov::Formatter::Terminal::TargetFileWriter
6
+ def initialize(targeted_application_file)
7
+ @targeted_application_file = targeted_application_file
8
+ end
9
+
10
+ def write_target_info_file
11
+ directory = 'tmp/simple_cov/formatter/terminal'
12
+ FileUtils.mkdir_p(directory)
13
+ File.write("#{directory}/target.txt", "#{@targeted_application_file}\n")
14
+ end
15
+ end
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SimpleCov; end
4
+ module SimpleCov::Formatter; end
5
+
6
+ # rubocop:disable Style/StaticClass
7
+ class SimpleCov::Formatter::Terminal
8
+ VERSION = '0.1.0'
9
+ end
10
+ # rubocop:enable Style/StaticClass
@@ -0,0 +1,69 @@
1
+ # frozen_string_literal: true
2
+
3
+ class SimpleCov::Formatter::Terminal ; end # rubocop:disable Lint/EmptyClass
4
+
5
+ require_relative 'terminal/config'
6
+ require_relative 'terminal/file_determiner'
7
+ require_relative 'terminal/r_spec_integration'
8
+ require_relative 'terminal/result_printer'
9
+ require_relative 'terminal/version'
10
+ require 'active_support/core_ext/object/blank'
11
+ require 'active_support/core_ext/string/filters'
12
+ require 'memoist'
13
+ require 'rouge'
14
+ require 'rspec/core'
15
+ require 'simplecov'
16
+
17
+ class SimpleCov::Formatter::Terminal
18
+ extend Forwardable
19
+ extend Memoist
20
+
21
+ def_delegators(
22
+ :file_determiner,
23
+ :executed_spec_file,
24
+ :executed_spec_files,
25
+ :targeted_application_file,
26
+ )
27
+ def_delegators(
28
+ :result_printer,
29
+ :print_coverage_info,
30
+ :print_info_for_no_executed_specs,
31
+ :print_info_for_nonexistent_application_target,
32
+ :print_info_for_undetermined_application_target,
33
+ )
34
+
35
+ class << self
36
+ extend Memoist
37
+
38
+ memoize \
39
+ def config
40
+ Config.new
41
+ end
42
+ end
43
+
44
+ def format(result)
45
+ if executed_spec_files.nil?
46
+ print_info_for_no_executed_specs
47
+ elsif targeted_application_file.nil?
48
+ print_info_for_undetermined_application_target
49
+ elsif File.exist?(targeted_application_file)
50
+ print_coverage_info(result)
51
+ else
52
+ print_info_for_nonexistent_application_target
53
+ end
54
+ end
55
+
56
+ private
57
+
58
+ memoize \
59
+ def file_determiner
60
+ SimpleCov::Formatter::Terminal::FileDeterminer.new
61
+ end
62
+
63
+ memoize \
64
+ def result_printer
65
+ SimpleCov::Formatter::Terminal::ResultPrinter.new(file_determiner)
66
+ end
67
+ end
68
+
69
+ SimpleCov::Formatter::Terminal::RSpecIntegration.setup_rspec
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'lib/simple_cov/formatter/terminal/version'
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = 'simple_cov-formatter-terminal'
7
+ spec.version = SimpleCov::Formatter::Terminal::VERSION
8
+ spec.authors = ['David Runger']
9
+ spec.email = ['davidjrunger@gmail.com']
10
+
11
+ spec.summary = 'Print detailed code coverage info to the terminal'
12
+ spec.description = 'Print detailed code coverage info to the terminal'
13
+ spec.homepage = 'https://github.com/davidrunger/simplecov-formatter-terminal'
14
+ spec.license = 'MIT'
15
+ spec.required_ruby_version = '>= 3.2.0'
16
+
17
+ spec.metadata['homepage_uri'] = spec.homepage
18
+ spec.metadata['source_code_uri'] = 'https://github.com/davidrunger/simplecov-formatter-terminal'
19
+ spec.metadata['changelog_uri'] =
20
+ 'https://github.com/davidrunger/simplecov-formatter-terminal/blob/master/CHANGELOG.md'
21
+
22
+ # Specify which files should be added to the gem when it is released.
23
+ # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
24
+ spec.files =
25
+ Dir.chdir(__dir__) do
26
+ `git ls-files -z`.split("\x0").reject do |f|
27
+ (f == __FILE__) || f.match(%r{\A(?:(?:bin|test|spec|features)/|\.(?:git))})
28
+ end
29
+ end
30
+ spec.bindir = 'exe'
31
+ spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
32
+ spec.require_paths = ['lib']
33
+
34
+ # Uncomment to register a new dependency of your gem
35
+ spec.add_dependency('activesupport', '>= 7.0.4', '< 8')
36
+ spec.add_dependency('memoist', '>= 0.16.2', '< 1')
37
+ spec.add_dependency('rouge', '>= 4.0.0', '< 5')
38
+ spec.add_dependency('rspec-core', '>= 3.11.0', '< 4')
39
+ spec.add_dependency('simplecov', '>= 0.21.2', '< 1')
40
+
41
+ # For more information and examples about making a new gem, check out our
42
+ # guide at: https://bundler.io/guides/creating_gem.html
43
+ spec.metadata['rubygems_mfa_required'] = 'true'
44
+ end
metadata ADDED
@@ -0,0 +1,169 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: simple_cov-formatter-terminal
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - David Runger
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2023-01-05 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: activesupport
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: 7.0.4
20
+ - - "<"
21
+ - !ruby/object:Gem::Version
22
+ version: '8'
23
+ type: :runtime
24
+ prerelease: false
25
+ version_requirements: !ruby/object:Gem::Requirement
26
+ requirements:
27
+ - - ">="
28
+ - !ruby/object:Gem::Version
29
+ version: 7.0.4
30
+ - - "<"
31
+ - !ruby/object:Gem::Version
32
+ version: '8'
33
+ - !ruby/object:Gem::Dependency
34
+ name: memoist
35
+ requirement: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - ">="
38
+ - !ruby/object:Gem::Version
39
+ version: 0.16.2
40
+ - - "<"
41
+ - !ruby/object:Gem::Version
42
+ version: '1'
43
+ type: :runtime
44
+ prerelease: false
45
+ version_requirements: !ruby/object:Gem::Requirement
46
+ requirements:
47
+ - - ">="
48
+ - !ruby/object:Gem::Version
49
+ version: 0.16.2
50
+ - - "<"
51
+ - !ruby/object:Gem::Version
52
+ version: '1'
53
+ - !ruby/object:Gem::Dependency
54
+ name: rouge
55
+ requirement: !ruby/object:Gem::Requirement
56
+ requirements:
57
+ - - ">="
58
+ - !ruby/object:Gem::Version
59
+ version: 4.0.0
60
+ - - "<"
61
+ - !ruby/object:Gem::Version
62
+ version: '5'
63
+ type: :runtime
64
+ prerelease: false
65
+ version_requirements: !ruby/object:Gem::Requirement
66
+ requirements:
67
+ - - ">="
68
+ - !ruby/object:Gem::Version
69
+ version: 4.0.0
70
+ - - "<"
71
+ - !ruby/object:Gem::Version
72
+ version: '5'
73
+ - !ruby/object:Gem::Dependency
74
+ name: rspec-core
75
+ requirement: !ruby/object:Gem::Requirement
76
+ requirements:
77
+ - - ">="
78
+ - !ruby/object:Gem::Version
79
+ version: 3.11.0
80
+ - - "<"
81
+ - !ruby/object:Gem::Version
82
+ version: '4'
83
+ type: :runtime
84
+ prerelease: false
85
+ version_requirements: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: 3.11.0
90
+ - - "<"
91
+ - !ruby/object:Gem::Version
92
+ version: '4'
93
+ - !ruby/object:Gem::Dependency
94
+ name: simplecov
95
+ requirement: !ruby/object:Gem::Requirement
96
+ requirements:
97
+ - - ">="
98
+ - !ruby/object:Gem::Version
99
+ version: 0.21.2
100
+ - - "<"
101
+ - !ruby/object:Gem::Version
102
+ version: '1'
103
+ type: :runtime
104
+ prerelease: false
105
+ version_requirements: !ruby/object:Gem::Requirement
106
+ requirements:
107
+ - - ">="
108
+ - !ruby/object:Gem::Version
109
+ version: 0.21.2
110
+ - - "<"
111
+ - !ruby/object:Gem::Version
112
+ version: '1'
113
+ description: Print detailed code coverage info to the terminal
114
+ email:
115
+ - davidjrunger@gmail.com
116
+ executables: []
117
+ extensions: []
118
+ extra_rdoc_files: []
119
+ files:
120
+ - ".release_assistant.yml"
121
+ - ".rspec"
122
+ - ".rubocop.yml"
123
+ - CHANGELOG.md
124
+ - Gemfile
125
+ - Gemfile.lock
126
+ - LICENSE.txt
127
+ - README.md
128
+ - Rakefile
129
+ - lib/simple_cov/formatter/terminal.rb
130
+ - lib/simple_cov/formatter/terminal/branch_coverage.rb
131
+ - lib/simple_cov/formatter/terminal/color_printing.rb
132
+ - lib/simple_cov/formatter/terminal/config.rb
133
+ - lib/simple_cov/formatter/terminal/file_determiner.rb
134
+ - lib/simple_cov/formatter/terminal/line_printer.rb
135
+ - lib/simple_cov/formatter/terminal/r_spec_integration.rb
136
+ - lib/simple_cov/formatter/terminal/rails_awareness.rb
137
+ - lib/simple_cov/formatter/terminal/result_printer.rb
138
+ - lib/simple_cov/formatter/terminal/spec_to_app_mapping.rb
139
+ - lib/simple_cov/formatter/terminal/target_file_writer.rb
140
+ - lib/simple_cov/formatter/terminal/version.rb
141
+ - simple_cov-formatter-terminal.gemspec
142
+ homepage: https://github.com/davidrunger/simplecov-formatter-terminal
143
+ licenses:
144
+ - MIT
145
+ metadata:
146
+ homepage_uri: https://github.com/davidrunger/simplecov-formatter-terminal
147
+ source_code_uri: https://github.com/davidrunger/simplecov-formatter-terminal
148
+ changelog_uri: https://github.com/davidrunger/simplecov-formatter-terminal/blob/master/CHANGELOG.md
149
+ rubygems_mfa_required: 'true'
150
+ post_install_message:
151
+ rdoc_options: []
152
+ require_paths:
153
+ - lib
154
+ required_ruby_version: !ruby/object:Gem::Requirement
155
+ requirements:
156
+ - - ">="
157
+ - !ruby/object:Gem::Version
158
+ version: 3.2.0
159
+ required_rubygems_version: !ruby/object:Gem::Requirement
160
+ requirements:
161
+ - - ">="
162
+ - !ruby/object:Gem::Version
163
+ version: '0'
164
+ requirements: []
165
+ rubygems_version: 3.4.2
166
+ signing_key:
167
+ specification_version: 4
168
+ summary: Print detailed code coverage info to the terminal
169
+ test_files: []